Haskell Двусмысленные вхождения - как избежать? - PullRequest
40 голосов
/ 01 февраля 2010

Я делаю следующее в GHCI:

:m + Data.Map
let map = fromList [(1, 2)]
lookup 1 map

GHCI знает, что map является (Map Integer Integer). Так почему он требует двусмысленности между 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

Ответы [ 2 ]

51 голосов
/ 01 февраля 2010

Типы явно различаются, но Haskell не допускает специальной перегрузки имен, поэтому вы можете выбрать только один 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 является одним из вторых, и авторы ожидают, что вы импортируете его квалифицированным.

[Изменить, чтобы включить комментарий ephemient]

Если вы хотите использовать Data.Map.lookup без префикса, вы должны скрыть Prelude.lookup, поскольку он неявно импортируется в противном случае:

import Prelude hiding (lookup) 
import Data.Map (lookup)

Это немного странно, но может быть полезно, если вы используете Data.Map.lookup всю связку, и все ваши структуры данных - это карты, а не списки.

22 голосов
/ 01 февраля 2010

В более общем плане, это то, что сначала смутило меня, поэтому позвольте мне повторить и подчеркнуть то, что Натан Сандерс сказал:

Haskell не допускает специальной перегрузки имен

Это верно по умолчанию, но на первый взгляд кажется неочевидным. Haskell допускает два стиля полиморфных функций :

  • Параметрический полиморфизм , который позволяет функции работать с произвольными типами структурно идентичным абстрактным образом
  • Специальный полиморфизм , который позволяет функции работать с любым из определенного набора типов структурно отличным, но, надеюсь, семантически идентичным образом

Параметрический полиморфизм - это стандартный (и предпочтительный, при выборе) подход на Хаскелле и родственных языках; ad-hoc полиморфизм является стандартом в большинстве других языков, он называется «перегрузка функций» и часто реализуется на практике путем написания нескольких функций с одинаковыми именами.

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

Таким образом, если в разных модулях определено несколько функций не-классового класса с одинаковыми именами, импорт обоих модулей без квалификации приведет к ошибкам при попытке использовать любую функцию. Комбинации Data.List, Data.Map и Data.Set являются особенно вопиющими в этом отношении, и поскольку части Data.List экспортируются Prelude, стандартная практика (как говорит Натан Сандерс) всегда импортировать остальные квалифицированный.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...