9

Я был возился с функцией runST. Который имеет тип (forall s. ST s a) -> a, и кажется, что пытаюсь использовать его любым способом, который не применяется непосредственно без какого-либо косвенного обращения, это довольно неприятно.Почему более ранние типы настолько хрупкие в Haskell

runST :: (forall s. ST s a) -> a 
const :: a -> b -> a 

так, подставляя a в const для forall s. ST s a вы должны получить тип const runST

const runST :: b -> (forall s. ST s a) -> a 

, но вместо этого GHC говорит, что не может сравниться с a(forall s. ST s a) -> a, но так как a буквально означает forall a. a что удовлетворенный каждым типом, я не вижу, что недействительно.

Как оказалось, используя \_ -> runST вместо этого фактически работает просто отлично и дает правильный тип.

Как только у меня был constST с правильным типом, я хотел посмотреть, могу ли я uncurry его, но неудивительно, что тоже ломается. Но кажется, что это на самом деле не стоит, хотя по общему признанию, этот случай кажется менее очевидным, чем предыдущая:

constST :: b -> (forall s. ST s a) -> a 
uncurry :: (a -> b -> c) -> (a, b) -> c 

Так то, конечно, a в uncurry может быть заменен на b в constST и b в uncurry мог заменить на (forall s. ST s a) на constST, а a в uncurry может быть заменен на c в constST. Это дает нам:

uncurry constST :: (b, forall s. ST s a) -> a 

Теперь, по общему признанию, этот тип является непроизводительным, который, как я знаю, довольно проблематичен. Но технически Just mempty также является нецелесообразным при непосредственном подстановке без перемещения неявной forall.

Just :: a -> Maybe a 
mempty :: forall a. Monoid a => a 
Just mempty :: Maybe (forall a. Monoid a => a) 

Но forall автоматически всплывали, чтобы дать вам:

Just mempty :: forall a. Monoid a => Maybe a 

Теперь, если мы делаем то же самое для uncurry constST мы должны получить разумный и, насколько я могу сказать, правильный тип:

uncurry constST :: (forall s. (b, ST s a)) -> a 

Какой у вас высокий ранг, но не способствующий.

Может кто-нибудь объяснить мне, почему в принципе ни одно из вышеприведенных действий не работает с GHC 8, есть ли что-то более фундаментальное, что делает вышеупомянутое очень сложным в общем случае? Потому что, если нет, похоже, было бы очень приятно иметь вышеупомянутую работу, если бы только избавиться от раздражающего специального корпуса $ исключительно ради runST.

На боковой ноте возможно, что вместо всех плавающих forall мы могли бы иметь ImpredicativeTypes, так как это нормально работает. Он правильно вводит тип для Just mempty как Maybe (forall a. Monoid a => a), но похоже, что на самом деле это не так просто.Я слышал, что вывод непроизводительного типа на самом деле не выполним, но будет ли он каким-то образом ограничивать тип вывода предикативными типами, за исключением случаев, когда вы предоставляете сигнатуры типов, указывающие иначе. Аналогично тому, как MonoLocalBinds делает локальные привязки мономорфными по умолчанию для вывода типа.

+1

Я не знаю полной основы теории, но тип вывода с более политическими политиками более высокого ранга является сложным - IIRC, неразрешимым. Следовательно, полиморфизм должен быть каким-то образом ограничен, чтобы сделать вывод возможным. Типичным ограничением является разрешение каждой переменной типа 'forall a. ... ', который будет создан только с монотипами (включая' data, newtype's). Импрессивные типы ослабляют это ограничение, но сейчас они не работают нормально в GHC. – chi

+3

@chi, мое смутное понимание состоит в том, что типы Haskell 98 + rank-2 могут быть выведены с трудом, но добавление более высоких рангов или расширение системного типа делает невозможным вывод. – dfeuer

ответ

11

Вы сами ответили на свой вопрос:

... путем замены a в const для forall s. ST s a ...

Это определение impredictive полиморфизма - способность инстанцирует переменная типа с polytype, которая (свободно) имеет тип с квантором forall в самой левой стороне этого типа.

С GHC trac страницы по теме:

GHC не (пока) поддерживает непредикативный полиморфизм

и, кроме того

Мы сделали различные попытки поддержать impredicativity , поэтому есть флаг -XImpredicativeTypes. Но он не работает и абсолютно не поддерживается. Если вы используете его, вы сами по себе; Я не обещаю, что произойдет.

Так что не используйте ImpredictiveTypes - это не поможет.

Теперь для подробностей - почему все конкретные примеры работают так же, как и они?

Вы заметили, что в выражении Just mempty не указывается инкриминирующий тип Maybe (forall a. Monoid a => a); вместо этого forall «выплыл». Вы также отметили, что выполнение того же процесса для uncurry constST дает тип «более высокий ранг, но не стимулирующий». GHC user guide это, что сказать, о более высоких рангах:

В целом, вывод типа для типов произвольного ранга неразрешим. ...

Для переменной лямбда-привязанной или связанной с регистром переменной x, либо программист предоставляет явный полиморфный тип для x, либо вывод типа GHC предполагает, что тип x не имеет в нем никаких фоллонов.

Таким образом, вы действительно должны помочь ему совсем немного, и что обычно исключает использование функций высшего порядка ALLtogether (обратите внимание, что выше не говорит ничего о произвольных приложениях, только о связанном переменном - и uncurry constST не имеют связанные переменные !). Правильный тип для Just mempty - это ранг 1, поэтому нет никаких проблем, связанных с ним, с или без подписи дополнительного типа.

Например, вы можете написать (forall s. (b, ST s a)) -> a функцию как таковую (на GHC 8.0.1, по крайней мере):

constST' :: forall a b . (forall s . (b, ST s a)) -> a 
constST' z = runST z' 
    where z' :: forall s . ST s a 
     z' = snd z 

, а также отметить, что вы не можете даже шаблон матч на паре, потому что это сразу инстанцирует связанный b тип вар:

constST' :: forall a b . (forall s . (b, ST s a)) -> a 
constST' (a,b) = _res 

Использование типизированных отверстий, вы получите:

* Found hole: _res :: a 
    Where: `a' is a rigid type variable bound by 
      the type signature for: 
      constST' :: forall a b. (forall s. (b, ST s a)) -> a 
* In the expression: _res 
    In an equation for constST': constST' (a, b) = _res 
* Relevant bindings include 
    b :: ST s0 a 
    a :: b 

Обратите внимание на типе b является ST s0 a для некоторой свежего типа переменнойs0, не требуется forall s . ST s a для runST. Невозможно вернуть старый тип.


Самое простое решение таких вещей, вероятно, определить newtype, как следует страница ПРОФ GHC:

newtype RunnableST a = RST (forall s . ST s a) 

rrunST :: RunnableST a -> a 
rrunST (RST a) = runST a 

И хранить ST действия, которые готовы работать в этом контейнере:

doSomething :: RunnableST X 
doSomething = RST $ do ... 
+0

Я думаю, что более или менее имеет смысл. Но я до сих пор не уверен, почему вывод типа, способный подставлять типы полисов, а затем плавать в форелях - это все безумие. Выводит 'const runST' правильно ли это проблема с ограниченным подмножеством вывода более высокого ранга? Я понимаю, что он не сможет безопасно создавать более высокие типы рангов из ничего, но как только у вас уже будет более высокий тип ранга, похоже, что это не было бы большой сделкой, чтобы позволить ему пройти немного , – semicolon

+2

Проблема с определением и внедрением «ограниченного подмножества» вывода более высокого ранга - вам нужна формальная семантика для него - без нечистоплотных типов, нет даже способа указать, какой тип 'const' создается в' const runST', потому что этот тип является нецелесообразным, хотя тип результата не является. Поэтому методу typechecker необходимо будет поддерживать непротиворечивые типы, решая проблему, которую мы пытались избежать в первую очередь. Кажется, такой ограниченный поднабор будет еще более ограниченным, чем ваш конкретный пример (который уже очень прост). – user2407038

+0

Хорошо, я думаю, что вижу, что вы говорите, это похоже на одно из них: простое на бумаге для одного примера и немного здравого смысла, но на самом деле ужасно из вычислительной и общей ситуации. Ну, надеюсь, в конечном итоге будет поддерживаться «ImpredicativeTypes». – semicolon

0

Для того чтобы написать const runST, вам необходимо активировать расширение Импрессивных типов (на GHCi: :set -XImpredicativeTypes).

+2

'ImpredicativeTypes' не поддерживается по GHC 8, и он не работает по разумным стандартам работы. –

+0

@ AndrásKovács, он был менее официально не поддержан еще задолго до этого. Если я не ошибаюсь, он вошел с коробчатыми типами, а затем застрял, как пьяный сторонник вечеринки, после чего был заменен OutsideIn (X). – dfeuer