Как написать функцию haskell без ввода-вывода в типе sig, скрывая изменения состояния
Я написал функцию в haskell, которая принимает несколько параметров, таких как Word32, String (игнорировать каррирование) и выводит IO Word32. Теперь это функция в истинном смысле: для одних и тех же входов выход всегда будет одинаковым. Там нет никаких побочных эффектов. Причина, по которой функция возвращает IO Word32 вместо Word32, заключается в том, что функция обновляет несколько 32-битных регистров линейного сдвига с обратной связью (lfsr) и другие регистры несколько раз в цикле, чтобы вычислить окончательный вывод Word32.
Мой вопрос заключается в следующем: учитывая, что эта функция фактически не имеет побочных эффектов, возможно ли скрыть эти обновления регистров внутри реализации функции, чтобы функция возвращала Word32, а не IO Word32? Если так, то как?
3 ответа
Да! Haskell может сделать это.
ST монада
Если вы фактически используете изменяемые состояния (регистры), которые полностью скрыты от наблюдателя за пределами функции, то вы находитесь в монаде ST, монаде только для эффектов памяти. Вы входите в мир ST через runST
и при выходе из функции все эффекты гарантированно не будут видны.
Это точно подходящая вычислительная среда для работы с локальным изменяемым состоянием.
Чисто функциональное состояние: Государственная монада
Однако, если вы на самом деле не мутируете регистры или ячейки, а скорее обновляете чисто функциональное значение много раз, доступна более простая среда: монада State. Это не допускает изменчивое состояние, но дает иллюзию локального состояния.
IO и unsafePerformIO
Наконец, если у вас есть локальные, изменчивые эффекты, как в ST
монада, но по тем или иным причинам вам понадобятся операции ввода-вывода в этом состоянии (например, через вызов FFI), вы можете смоделировать ST
монада, с почти такой же безопасностью, используя unsafePerformIO
, вместо runST
, чтобы ввести локальную среду ввода-вывода. Поскольку у монады IO нет хороших типов для обеспечения абстракции, вам нужно будет вручную убедиться, что побочные эффекты не будут заметны.
Если вы импортировали эту функцию с помощью FFI, просто удалите IO
из типа возврата. Остальное, использовать unsafePerformIO :: IO a -> a
от System.IO.Unsafe
, Обратите внимание, что эта функция является одной из самых опасных функций в Haskell. Не используйте его, если вы не уверены в последствиях. Но для твоих целей все в порядке.
Да, это было бы законным использованием unsafePerformIO. Но это нормально, если вы действительно уверены, что видимых эффектов нет.