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

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

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

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 } выглядит неудобно.

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

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

Ответ 1

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

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

fooobar.com/questions/249262/... аналогичен, и некоторые из ответов там также могут быть полезны для вас.

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

Квалифицированный импорт и псевдонимов обычно достаточно, чтобы решить эту проблему.

Ответ 2

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

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

data Foo a = Foo { foo_value :: a }

data Bar a = Bar { bar_value :: a }

или даже

data Apocalypse a = A { ap_value :: a }

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

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

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

Ответ 3

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

class Fieldable a where
     field :: a -> b

instance Fieldable (a,b) where
     field = fst

Etc.