Сигнатура типа Haskell от u f = f.f сильнее, чем хотелось бы

Я написал следующую простую функцию

u f=f.f

В соответствии с ghci это имеет сигнатуру типа

u :: (b -> b) -> b -> b

Однако эта подпись типа слишком строгая. Haskell гарантирует, что наш ввод будет иметь тип (b -> b), когда это не обязательно должно быть. Например, функция (:[]) имеет сигнатуру типа

(:[]) :: a -> [a]

Это не форма (b -> b) (если вы не разрешаете бесконечные типы) и, следовательно, не может быть передана u. Однако вы можете составить (:[]) с собой.

g=(:[]).(:[])

Это работает и имеет тип

(:[]).(:[]) :: a -> [[a]]

Поэтому я должен в принципе передать это u.

Я попытался написать новую подпись типа, чтобы заменить сгенерированную подпись, но я не мог придумать способ выражения требований функции. Я всегда придумываю подпись того же типа, которую предоставляет компилятор. Есть ли сигнатура типа, которую мы можем дать, чтобы ослабить u, чтобы функции, подобные (:[]), могли быть переданы ей?

Ответы

Ответ 1

Существует множество различных функций, которые могут делать это для конкретных случаев, но не работают в целом.

u1 :: (forall a. a -> f a) -> b -> f (f b)
u2 :: (forall a. f a -> a) -> f (f b) -> b

и бесконечно больше. Но функция

u f x = f (f x)

не имеет самого общего типа в Haskell, когда RankNTypes находится в игре. Как отмечает заводчик, существуют системы типов, которые могут дать u принципиальный тип желаемого типа, но они принимают расширение системы типов в совершенно другом направлении от тех, на которые ориентировались дизайнеры Haskell.