Почему я не могу записать значение в IORef, но могу его прочитать

В haskell мне нужна глобальная переменная, поэтому я решил использовать слот IORef, вот мой план:

memo :: IORef Int
memo = unsafePerformIO $ newRefInt 9999

evaluate ARGs s = do
  v <- Right $ unsafePerformIO $ readIORef memo
  val <- Right $ VInt v
  return $ (val, s)

evaluate (Call funcID exp) s = do
...
Right $ writeIORef memo 100
...

Мой план заключается в том, что когда исполнитель оценивает узел «Вызов», он сохраняет параметр в слот. Затем, когда оценивается узел «ARGs», этот слот для заметок будет прочитан.

Но что бы я ни делал, я просто могу прочитать 9999, но не могу записать в этот слот новое значение.

Даже я пробовал:

memo :: IORef Int
memo = unsafePerformIO $ newRefInt 9999

evaluate ARGs s = do
  Right $ writeIORef memo 100
  v <- Right $ unsafePerformIO $ readIORef memo
  val <- Right $ VInt v
  return $ (val, s)

Все равно получится, что memo = 9999. Почему?


person user2990614    schedule 04.12.2013    source источник
comment
Существует очень мало допустимых вариантов использования unsafePerformIO. Если вы обнаружите, что используете его, найдите другой способ сделать это с помощью монады IO. Это вызовет больше проблем, чем решит обычный код. Это только там для очень специализированных случаев.   -  person bheklilr    schedule 05.12.2013
comment
Напомним, что unsafePerformIO был введен только с интерфейсом чужих функций: для выполнения чистых вычислений в чистом коде, где они должны быть, когда компилятор, однако, не может доказать его правильность, потому что вы вызываете функции, написанные на другом языке. (побочный) язык.   -  person leftaroundabout    schedule 05.12.2013
comment
В дополнение к другим проблемам вы должны добавить прагму {-# NOINLINE memo #-}. Без этого компилятор может (почти наверняка будет) встроить ссылки на memo, что приведет к тому, что разные части вашего кода будут обращаться к разным IORef.   -  person John L    schedule 05.12.2013


Ответы (1)


Потому что письмо тоже находится в монаде IO. Во-первых, такое количество unsafePerformIO просто плохо. unsafePerformIO не следует использовать в обычном коде.

Прямо сейчас вы создаете действие для записи в IORef типа IO (), оборачиваете его в конструктор Right, а затем выбрасываете, вы никогда его не используете.

Вы также не можете unsafePerformIO этого, так как вы не строги в значении созданного вами Either значения. Вот почему unsafePerformIO плохо, невероятно сложно рассуждать о том, когда и если что-то произойдет.

Вместо этого попробуйте

 evaluate ARGs s = do
    liftIO $ writeIORef memo 100
    v <- liftIO $ readIORef memo
    val <- return $ VInt v
    return $ (val, s)

И используйте преобразование монады EitherT, чтобы вставить туда IO.

person Daniel Gratzer    schedule 04.12.2013
comment
@GabrielGonzalez Да, я просто забыл отредактировать исходный фрагмент. - person Daniel Gratzer; 05.12.2013