Вывод типа Haskell для функторов
В последнее время я играл с Haskell и, в частности, с целой концепцией функторов. Чем больше я погружаюсь в это, тем больше моментов я получаю, и это определенно щекочет мои рецепторы допамина.
Проблема, с которой я застрял, заключается в следующем. Вот код, который работает, он поднимает функцию, а затем применяет ее сначала к значению ввода-вывода, а затем к списку.
replicator1 =
fmap (replicate 3)
replicator2 =
fmap (replicate 3)
main = do
replicated <- replicator1 getLine
print (replicator2 replicated)
Очень заманчиво написать это в более сжатой форме, то есть:
replicator =
fmap (replicate 3)
main = do
replicated <- replicator getLine
print (replicator replicated)
Часть меня говорит, что это концептуально правильно, так как replicator
должен быть применим как к IO, так и к экземплярам List, но, будучи строго типизированным языком, Haskell не позволяет мне этого делать. Я думаю, что в значительной степени понимаю, почему это происходит.
Вопрос в том, можно ли как-нибудь приблизиться к последнему варианту? Или нормально жить с первым?
Спасибо!
1 ответ
Ваш код на самом деле в порядке, за исключением того, что вы столкнулись с ужасным ограничением мономорфизма, которое не давало Хаскеллу выводить наиболее общий возможный тип для replicator
,
По сути, с ограничением Haskell не будет выводить полиморфные типы для привязок, которые не похожи на функции. Это означает, что нужно выбрать конкретный функтор, такой как []
или же IO
за replicate
и вызывает ошибку, если вы пытаетесь использовать его в двух разных контекстах.
Вы можете заставить свой код работать тремя способами:
отключить ограничение мономорфизма: добавить
{-# LANGUAGE NoMonomorphismRestriction #-}
в верхней части вашего модуля.делать
replicator
выглядеть как функция:replicator x = fmap (replicate 3) x
добавить явные сигнатуры типов в ваш код
replicator :: Functor f => f a -> f [a] replicator = fmap (replicate 3)
Третий вариант самый идиоматичный. Хороший стиль Haskell предполагает добавление явных сигнатур типов ко всем вашим идентификаторам верхнего уровня. Тем не менее, полезно знать два других варианта, чтобы понять, что происходит, и иметь возможность писать быстрые и грязные одноразовые сценарии в Haskell, не беспокоясь о сигнатурах типов.