Haskell Неоднозначные события - как этого избежать?
В GHCI я делаю следующее:
:m + Data.Map
let map = fromList [(1, 2)]
lookup 1 map
GHCI знает, что карта является (целочисленным целым числом). Итак, почему он требует двусмысленности между Prelude.lookup и Data.Map.lookup, когда тип ясен, и я могу избежать?
<interactive>:1:0:
Ambiguous occurrence `lookup'
It could refer to either `Prelude.lookup', imported from Prelude
or `Data.Map.lookup', imported from Data.Map
> :t map
map :: Map Integer Integer
> :t Prelude.lookup
Prelude.lookup :: (Eq a) => a -> [(a, b)] -> Maybe b
> :t Data.Map.lookup
Data.Map.lookup :: (Ord k) => k -> Map k a -> Maybe a
Ответы
Ответ 1
Типы явно различаются, но Haskell не разрешает перегрузку имен ad-hoc, поэтому вы можете выбрать только один lookup
, который будет использоваться без префикса.
Типичным решением является импорт Data.Map
квалифицированных:
> import qualified Data.Map as Map
Тогда вы можете сказать
> lookup 1 [(1,2), (3,4)]
Just 2
> Map.lookup 1 Map.empty
Nothing
Обычно библиотеки Haskell избегают повторного использования имен из Prelude или повторно используют целую кучу. Data.Map
является одним из вторых, и авторы ожидают, что вы импортируете его квалифицированным.
[Изменить, чтобы включить эфемерный комментарий]
Если вы хотите использовать Data.Map.lookup
без префикса, вам нужно скрыть Prelude.lookup
, поскольку он неявно импортирован в противном случае:
import Prelude hiding (lookup)
import Data.Map (lookup)
Это немного странно, но может быть полезно, если вы используете Data.Map.lookup
целую кучу, а ваши структуры данных - это все карты, а не алисты.
Ответ 2
В несколько более общем примечании, это то, что меня сначала смутило - так, позвольте мне повторить и подчеркнуть то, что сказал Натан Сандерс:
Haskell не разрешает перегрузку имен ad-hoc
Это верно по умолчанию, но сначала кажется неожиданно неочевидным. Haskell допускает два стиля полиморфные функции:
- Параметрический полиморфизм, позволяющий функционировать на произвольных типах структурно идентичным абстрактным образом.
- Ad-hoc-полиморфизм, который позволяет функции работать с любым из определенного набора типов в структурно отличном, но, надеюсь, семантически идентичном образом.
Параметрический полиморфизм является стандартным (и предпочтительным выбором) в Haskell и родственных языках; ad-hoc-полиморфизм является стандартом на большинстве других языков, идя по именам, таким как "перегрузка функций", и часто реализуется на практике путем написания нескольких функций с тем же именем.
Ad-hoc-полиморфизм включен в Haskell по типам классов, которые требуют, чтобы класс был определен со всеми его связанными ad-hoc-полиморфными функциями, и экземпляры, которые должны быть явно объявлены для типов, используемых разрешением перегрузки. Функции, определенные за пределами объявления экземпляра, никогда не являются ad-hoc полиморфными, даже если их типы достаточно различны, что ссылка будет недвусмысленной.
Итак, когда несколько разных типов классов с одинаковыми именами определены в разных модулях, импорт обоих модулей неквалифицирован, это приведет к ошибкам, если вы попытаетесь использовать любую функцию. Комбинации Data.List
, Data.Map
и Data.Set
являются особенно вопиющими в этом отношении, и поскольку части Data.List
экспортируются прелюдией, стандартная практика (как говорит Натан Сандерс) всегда импортировать другие квалифицированный.