Как повторно использовать переменную типа во внутренней декларации типа
Как часть моего учебного процесса Haskell, мне нравится явно вводить объявления типов для функций. Я хотел бы иметь возможность сделать это для функций, определенных в предложении where, но я не знаю, как указать, что переменная типа в предложении where должна обозначать тот же тип, что и переменная типа во внешнем объявлении типа. Например, следующий код:
foo :: (a -> a) -> a -> a
foo f arg = bar arg
where
bar :: a -> a
bar a = f a
дает эту ошибку:
src\Test.hs:7:14:
Couldn't match expected type `a' against inferred type `a1'
`a' is a rigid type variable bound by
the type signature for `foo' at src\Test.hs:3:8
`a1' is a rigid type variable bound by
the type signature for `bar' at src\Test.hs:6:11
In the first argument of `f', namely `a'
In the expression: f a
In the definition of `bar': bar a = f a
Как я могу выразить, что первый аргумент bar должен быть того же типа, что и второй аргумент foo, так что я могу применить f к нему?
Спасибо.
Ответы
Ответ 1
Я думаю, вы можете сделать это в целом с помощью ScopedTypeVariables, который поддерживает GHC. Это, безусловно, компилируется:
{-# LANGUAGE ScopedTypeVariables #-}
foo :: forall a. (a -> a) -> a -> a
foo f arg = bar arg
where
bar :: a -> a
bar a = f a
Обратите внимание на "forall a."
Ответ 2
Существует еще одно обходное решение. Вместо ссылки f
внутри внутренней функции bar
, продолжите bar
, чтобы принять f
в качестве первого аргумента и использовать частичное приложение в родительском.
foo :: (a -> a) -> a -> a
foo f arg = (bar f) arg
where
bar :: (a -> a) -> a -> a
bar f a = f a
В качестве других ответов не требуется ScopedTypeVariables или явный код проверки типов.
Объяснение
Для ясности измените параметр типа в bar
на b
, а также переименуйте его аргумент.
foo :: (a -> a) -> a -> a
foo f arg = bar arg
where
bar :: b -> b
bar x = f x
Haskell жалуется, потому что bar
аннотируется как b -> b
(для любого произвольного типа b
), но f x
пытается применить аргумент типа b
к функции типа a -> a
(для конкретный, связанный a
). Другими словами, внутренняя функция не такая общая, как рекламирует ее аннотация.
Передача f
в bar
означает, что для выражения (bar f)
переменная типа b
привязана к тому же типу, что и a
.
Еще проще
И, наконец, без изменения чего-либо еще, если вы хотите опустить подпись типа для внутренней функции bar
, Haskell выведет свой тип точно так, как вы хотите. То есть, поскольку bar
применяется f
из родительской функции foo
, тип bar
будет повторно использовать параметр типа a
из типа foo
.
foo :: (a -> a) -> a -> a
foo f arg = bar arg
where
-- Type: bar :: a -> a
bar a = f a
Ответ 3
Этот ответ на другой вопрос показывает трюк для использования, если вы не хотите использовать расширение ScopedTypeVariables.
Ответ 4
http://www.haskell.org/haskellwiki/Scoped_type_variables
http://www.haskell.org/ghc/docs/latest/html/users_guide/other-type-extensions.html#scoped-type-variables