Подтвердить что ты не робот

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
4b9b3361

Ответ 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 экспортируются прелюдией, стандартная практика (как говорит Натан Сандерс) всегда импортировать другие квалифицированный.