Доступ к параметру, где функция предложения в Haskell - PullRequest
0 голосов
/ 03 июня 2018

Я понимаю, что с "обычными" функциями я могу получить доступ к параметрам на верхнем уровне из функции, определенной в предложении where.Однако, когда я пытаюсь сделать то же самое с монадическими функциями, я получаю ошибку во время компиляции.

Это работает:

module Main where

import Control.Monad.ST (ST, runST)
import Data.Array.ST    (STUArray, newArray, readArray)

main :: IO ()
main = do
  print $ runST $ do
              arr <- newArray (0, 9) 0
              checkArr arr
  return ()

checkArr :: STUArray s Int Int -> ST s Bool
checkArr arr = do
  val <- readArr arr
  return $ val == 0
  where
    readArr :: STUArray s Int Int -> ST s Int
    readArr arr = readArray arr 0

Но это не так:

module Main where

import Control.Monad.ST (ST, runST)
import Data.Array.ST    (STUArray, newArray, readArray)

main :: IO ()
main = do
  print $ runST $ do
              arr <- newArray (0, 9) 0
              checkArr arr
  return ()

checkArr :: STUArray s Int Int -> ST s Bool
checkArr arr = do
  val <- readArr
  return $ val == 0
  where
    readArr :: ST s Int
    readArr = readArray arr 0

Это приводит к следующей ошибке:

No instance for (Data.Array.Base.MArray (STUArray s) Int (ST s1)) arising from a use of ‘readArray’

Почему я не могу получить доступ к параметру arr checkArr в readArr?

1 Ответ

0 голосов
/ 03 июня 2018

Первое замечание: сообщение об ошибке говорит о том, что отсутствует объявление экземпляра, а не неизвестное имя переменнойКроме того, в качестве первой подсказки обратите внимание, что сообщение об ошибке содержит два различных тега ST: s, возникающих из STUArray (что связано с параметром arr вашей функции), и еще один s1 in ST s1.Откуда это s1?Действительно, в вашей программе об этом нет упоминания!

В качестве второго совета попробуйте удалить объявление подписи для readArr, чтобы предложение where выглядело как

  where
    readArr = readArray arr 0

Вдруг этот тип проверяет и работает просто отлично.Что происходит?

Ответ таков: когда вы писали readArr :: ST s Int, вы имели в виду s из сигнатуры checkArr, но правила области видимости таковы, что фактически вводится новая переменная типа, котораяПроверщик типов переименовывает вас в s1, а s не обязательно совпадает с s1, следовательно, ошибка типа.Если вы пропустите эту сигнатуру, то средство проверки типов сможет получить правильный тип.

Если вы действительно хотите записать тип в where -привязку, как это, есть расширение для этого!ScopedTypeVariables позволяет вам ссылаться на свободную переменную (которая является сигнатурой s из checkArr) внутри функции.Итак, добавьте {-# LANGUAGE ScopedTypeVariables #-} и сообщите контролеру типов, что вы хотите его ограничить, написав явный forall в сигнатуре верхнего уровня: checkArr :: forall s. STUArray s Int Int -> ST s Bool.После этого ваш код будет проверен.

...