Чтобы начать с конца, решение включает ScopedTypeVariables
и использует явный forall
в подписи mapSupply
, например:
{-# LANGUAGE ScopedTypeVariables #-} -- Put this at the top of your file.
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
mapSupply mapFunc (S supFuncA) = S supFuncB where
supFuncB :: Stream s -> (b, Stream s)
supFuncB strm = let (la, strms) = supFuncA strm in
((mapFunc la), strms)
Ниже приведено объяснение того, почемуэто необходимо.
Когда вы пишете такую подпись:
mapSupply :: (a -> b) -> Supply s a -> Supply s b
GHC фактически видит это:
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
forall
, чтообычно может быть неявным, говорит, что a
, b
и s
могут быть чем угодно - mapSupply
- это полиморфная функция, и поэтому любой, кто ее использует, может свободно выбирать любые конкретные типыдля трех типов переменных.Если явно указать forall
, второе определение будет выглядеть следующим образом:
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
mapSupply mapFunc (S supFuncA) = S supFuncB where
supFuncB :: forall s b. Stream s -> (b, Stream s)
supFuncB strm = let (la, strms) = supFuncA strm in
((mapFunc la), strms)
В соответствии с этим a
, b
и s
в mapSupply
могут быть чем угодно, аТо же самое касается s
и b
в supFuncB
.Это проблема, хотя.Например, определение включает strms
, тип которого s
... за исключением того, что s
, который отображается, потому что вы используете supFuncA
, равен , а не тот из supFuncB
подпись, а точнее та, что из mapSupply
подписи.Хотя s
из mapSupply
, в принципе, может быть чем угодно, как я уже отмечал ранее, как только вы действительно выберете s
с помощью mapSupply
, s
из supFuncB
должно совпадать с ним.В этом случае forall
в сигнатуре supFuncB
неуместен, поскольку переменные его типа не могут быть ничем.Становится легче увидеть, если мы переименуем переменные типа из supFuncB
, чтобы их имена не конфликтовали с именами из mapSupply
(учитывая forall
, это должно быть допустимым шагом для этого):
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
mapSupply mapFunc (S supFuncA) = S supFuncB where
supFuncB :: forall s1 b1. Stream s1 -> (b1, Stream s1)
supFuncB strm = let (la, strms) = supFuncA strm in
((mapFunc la), strms)
(GHC делает это внутренне, что объясняет, почему в сообщении об ошибке вы указали переменную типа s1
.)
Эта проблема возникла только из-за подписи, добавленной к supFuncB
, котораяввел неявное forall
.Без подписи GHC делает то, что вы хотите, не обобщая типы из supFuncB
- в этом случае это не полиморфно, а скорее мономорфно в переменных типа a
, b
и s
, которые позаимствованы у mapSupply
.Расширение ScopedTypeVariables
позволяет восстановить это поведение при записи сигнатуры типа для supFuncB
.Когда он включен, использование явного forall
для переменных типа в сигнатуре приведет к тому, что любые переменные типа с тем же именем в соответствующем определении относятся к одной и той же вещи (если только они не находятся под forall
в собственных подписях).Другими словами, благодаря этому становится возможным ссылаться на переменные из внешней сигнатуры в любом месте области действия соответствующего определения, что оправдывает название расширения.