Конфликты имен между метками полей разных типов данных в Haskell

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

Если у меня есть эти три модуля:

module One where

data Foo a = Foo { value :: a }

----

module Two where

data Bar a = Bar { value :: a }

----

module Three where

import One
import Two

foo = Foo { value = 42 }  -- compile error here
n = value foo  -- and here

неквалифицированные ссылки на value в модуле Three считаются неоднозначными, даже если только одно из двух импортированных имен имеет смысл в этом контексте. (На языке OO ссылки на foo.value и bar.value будут однозначными.)

Конечно, я могу устранить неоднозначность, написав Foo { One.value = 42 }, но это выглядит неловко. Я также могу назвать поля по-разному, например "fooValue" и "barValue", но избыточность в Foo { fooValue = 42 } тоже выглядит неудобной.

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

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


person Wyzard    schedule 19.07.2010    source источник
comment
Незначительный момент: в вашем определении Bar отсутствует конструктор.   -  person Travis Brown    schedule 19.07.2010
comment
@ Трэвис Браун: Ой, это была опечатка. Спасибо, что указали на это.   -  person Wyzard    schedule 19.07.2010


Ответы (3)


Расширение GHC -XDisambiguateRecordFields позволит foo = Foo { value = 42 } (но не n = value foo).

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

Этот вопрос StackOverflow аналогичен, и некоторые ответы там также могут быть полезны для тебя.

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

Для решения этой проблемы обычно достаточно квалифицированного импорта и псевдонимов.

person Travis Brown    schedule 19.07.2010

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

Я работал только с несколькими опытными разработчиками Haskell, и они делают ужасные вещи вроде

data Foo a = Foo { foo_value :: a }

data Bar a = Bar { bar_value :: a }

или даже

data Apocalypse a = A { ap_value :: a }

В общем, у меня такое ощущение, что многие старые хаскелеры не любят полные имена и действительно хотят притвориться, что в мире есть только одно большое пространство имен, прямо из темных веков. (Было время, когда компиляторы C имели такие же ограничения на имена полей, поэтому режим в struct stat называется st_mode, а не просто mode.)

Вы можете перегружать имена классами типов, но опытные разработчики, которых я знаю, не любят бесплатные классы типов. Я никогда не могу понять, когда они думают, что типовой класс будет бесплатным или нет.

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

person Norman Ramsey    schedule 19.07.2010
comment
Если вам на самом деле не нужны полиморфные селекторы записей, то для меня имеет смысл не использовать их принудительно только для небольшого синтаксического удобства. Даже если вы можете использовать определенные вещи, мне кажется, что требовать общего имени для редко используемого поля, особенно для того, к которому обычно можно получить доступ только через вспомогательную функцию, - это плохой тон. - person sclv; 20.07.2010
comment
Я использовал квалифицированные имена, насколько мог. Сам язык прекрасно с этим справляется; единственная проблема в том, что пикша становится намного менее полезной с квалифицированными именами, потому что она все еще предполагает одно большое пространство имен. (Вы можете сделать так, чтобы Haddock показывал имена типов уточненными, но он все равно будет показывать неквалифицированные гиперссылки.) - person Omari Norman; 24.10.2014

Вы можете рассмотреть типовой класс, если для всех этих типов есть общая функция доступа. Например.

class Fieldable a where
     field :: a -> b

instance Fieldable (a,b) where
     field = fst

И Т. Д.

person Don Stewart    schedule 19.07.2010
comment
Могут ли имена полей типа данных быть частью экземпляра класса? Если я сделаю class Valued a b where value :: a -> b, чтобы связать с ним два моих типа, мне пришлось бы сделать что-то вроде instance Valued (Foo a) a where value = value, а это недопустимо (два значения value конфликта). Кажется, я не могу определить функцию как часть экземпляра класса, если ее имя уже определено заранее. - person Wyzard; 19.07.2010