2013-11-14 2 views
7

Я хочу что-то вродеПрактические последствия runST против unsafePerformIO

f :: [forall m. (Mutable v) (PrimState m) r -> m()] -> v r -> v r -- illegal signature 
f gs x = runST $ do 
    y <- thaw x 
    foldM_ (\_ g -> g y) undefined gs -- you get the idea 
    unsafeFreeze y 

Я по существу в том же положении, я был в this question где Витус комментировал:

[I] F вы хотите сохранить полиморфный функции внутри какой-либо структуры, вам нужен либо специализированный тип данных (например, newtype I = I (forall a. a -> a)) или ImpredicativeTypes.

Также см. this question. Проблема в том, что это оба действительно уродливые решения. Итак, я придумал третью альтернативу, которая заключается в том, чтобы избежать полиморфизма вообще, запустив то, что «должно» быть вычислением ST в IO. Таким образом f становится:

f :: [(Mutable v) RealWorld r -> IO()] -> v r -> v r 
f gs x = unsafePerformIO $ do 
    y <- thaw x 
    foldM_ (\_ g -> g y) undefined gs -- you get the idea 
    unsafeFreeze y 

Я чувствую себя немного грязным для перехода на unsafeIO маршрута сравнивается с «безопасным» ST маршрута, но если моя альтернативой является оберткой или непредикативными типами ... Видимо, I'm not alone.

Есть ли причины, по которым я не должен использовать unsafePerformIO здесь? В этом случае, действительно ли это небезопасно? Есть ли соображения производительности или что-то еще, о чем я должен знать?

-------------- EDIT ----------------

Ответ ниже показывает мне, как обойти эта проблема в целом, что здорово. Но меня все еще интересует исходный вопрос (implicaitons of runST vs unsafePerformIO при использовании изменяемых векторов) для образовательных целей.

+3

Мне любопытно, почему вы думаете, что коробки нового типа являются таким уродливым решением? Это, откровенно говоря, упрощает чтение кода, «PolyModifier» легче понять, чем 'forall m. (Mutable v) (PrimState m) r -> m() '. Помимо этого, вы отказываетесь от некоторых заверений, которые дают вам системы типов. Трудно сказать, есть ли у вас здесь, вы не показали нам весь код. Но теперь вы делаете утверждения типа «x никогда больше не будете снова просматриваться», так как вы просто сбиваете чистую структуру – jozefg

+0

Конечно, подпись для функции * one * лучше, но везде я называю эту функцию, я буду иметь «отобразить PM» в список функций. Мне также не нравилось обертывать что-то, у которого нет причин быть обернутым с точки зрения абстракции: это просто функция, ничего особенного в этом. – crockeea

+0

У меня были некоторые подобные ситуации при работе с изорекурсивными типами, и у вас есть * для использования нового типа. Это обычная практика и в объективе. Я бы взял это на unsafePeformIO, нажатие клавиш <часы отладки. – jozefg

ответ

4

Я не могу сказать, что я все еще понимаю проблему, но следующий файл компилируется без ошибок в GHC 7.6.2. Он имеет то же тело, что и ваш первый пример (и, в частности, он вообще не вызывает unsafePerformIO); основное отличие состоит в том, что forall перемещается за пределы всех конструкторов типов.

{-# LANGUAGE RankNTypes #-} 
import Control.Monad 
import Control.Monad.Primitive (PrimState) 
import Control.Monad.ST 
import Data.Vector.Generic hiding (foldM_) 

f :: Vector v r => (forall m. [Mutable v (PrimState m) r -> m()]) -> v r -> v r 
f gs x = runST $ do 
    y <- thaw x 
    foldM_ (\_ g -> g y) undefined gs 
    unsafeFreeze y 

Теперь давайте решать ST против IO вопрос о. Причина, по которой она называется unsafePerformIO, а не unusablePerformIO, связана с тем, что она не может быть проверена компилятором: вещь, которую вы используете unsafePerformIO, должна вести себя так, как если бы она была прозрачной. Начиная с ST действия имеют доказательство (проверено компилятором), что они ведут себя прозрачно при выполнении с runST, это означает, что нет никакой опасности при использовании unsafePerformIO по коду, который будет выглядеть по типу ST, чем при использовании runST.

BUT: существует опасность с точки зрения программного обеспечения. Поскольку доказательство больше не проверяется компилятором, гораздо проще для будущего рефакторинга нарушать условия, при которых можно безопасно использовать unsafePerformIO. Поэтому, если можно избежать этого (как кажется, здесь), вы должны приложить все усилия для этого. (Кроме того, «нет никакой опасности» не означает «нет никакой опасности»: вызов, который вы вызываете, имеет свою собственную брешь, которую вы должны удовлетворить, но тогда вам уже пришлось удовлетворить эту бремени для ST правильный код.)

+0

Я вижу, что я делал неправильно: у меня есть ограничения (например, 'PrimMonad m'), но оставили их * внутри * списка, когда я вытащил' forall m'. Спасибо за это! – crockeea

+0

Я * все еще * заинтересован в 'runST' против' unsafePerformIO', только для образовательных целей на данный момент. – crockeea

+0

@ Эрик Хорошо, я добавил короткий комментарий об этом. –

Смежные вопросы