В Haskell как отличить guish изменяемые ссылки от обычных переменных в определениях функций monadi c - PullRequest
3 голосов
/ 03 мая 2020

Допустим, вы хотите написать некоторую функцию с состоянием в Haskell. Вы должны использовать стиль monadi c следующим образом: (используя любую монаду состояний)

f :: x -> k -> (ST s) r

Таким образом, это означает, что, по сути, функция принимает некоторый ввод x и k, может использовать и / или измените мир, чтобы вычислить возвращаемое значение r.

Предположим, x является структурой с состоянием, которая может быть изменена с помощью f. Предположим, k - это просто простой тип ключа, используемый, например, для доступа к чему-либо в x. Самому k будет назначен простой тип номера позже, но нам не нужно сейчас определять его тип.

Так что, по сути, я знаю, что x изменчива, а k неизменна. Проблема только в том, чтобы посмотреть на сигнатуру f, мы не можем этого сказать, поэтому, если f встречается в теле более сложного монадического c кода, мы не можем очень хорошо рассуждать об этих переменных.

Пример:

g :: x -> k -> (ST s) r
g a i = do
    ...
    f a i --  I don't know if i :: k depends on state
    ... --- I don't know if i was changed by f

Я имею в виду, что если мне дали переменную i неизвестного типа k, я не знаю, зависит ли она от s и может ли на его значение повлиять вызов f. Эта проблема, конечно же, не существует при написании чистых функций, поскольку все является неизменным.

Существует ли способ удобного аннотирования и, что более важно, статического обеспечения того, что k останется неизменным в монаде ST при вызове f?

1 Ответ

2 голосов
/ 03 мая 2020

Внутри ST вы определенно можете сказать, что является изменяемым: Int всегда является неизменяемым целым числом, тогда как STRef s Int является (неизменяемой) ссылкой на изменяемый Int.

Следовательно,

f :: STRef s Int -> String -> (ST s) Bool

может (читать и) изменять Int, указанный для первого аргумента, но может читать только неизменяемую строку, переданную как второй аргумент.

Кроме того, f может создавать (и изменять) новые STRef s ссылки на вновь назначенные значения. Он также может изменять другие значения, если f было определено с использованием ссылки на такие значения. Например,

bar :: forall s . ST s ()
bar = do
   x_ref <- newSTRef "hello"
   let f :: STRef s String -> String -> ST s ()
       f y_ref str = do
         y <- readSTRef y_ref
         writeSTRef x_ref y
         writeSTRef y_ref (y ++ " change " ++ str)
   ...

вызов f изменит как строку, которая изначально была установлена ​​на "hello", так и строку, ссылка на которую передана на f.

В вашем собственном примере :

g :: x -> k -> (ST s) r
g a i = do
    ...
    f a i --  I don't know if i :: k depends on state
    ... --- I don't know if i was changed by f

Если i :: k не было ссылкой, оно все равно имеет то же значение. Если бы это была ссылка, указанное значение могло бы быть изменено на f a i.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...