Прибывая к 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 }
выглядит неловким также.
Это - действительно особый случай более общего вопроса функций в различных модулях, которые имеют то же имя, но воздействуют на различные, несвязанные типы. Я, кажется, сталкиваюсь с ним чаще с именами полей, все же. Например, у меня есть несколько типов данных, не связанных классом типа, но часто используемых вместе, которые содержат значения цвета, таким образом, я хотел бы, чтобы у каждого было поле, названное "цветом".
Как опытные разработчики Haskell называют вещи и организуют их в модули, для предотвращения этого типа ситуации?
Расширение GHC -XDisambiguateRecordFields
разрешит foo = Foo {value = 42}
(но не n = value foo
).
Существует большой объем литературы о недостатках текущей системы записи Haskell и кандидатах на ее замену, а также о нескольких библиотеках, которые сейчас пытаются предоставить более хорошие решения. fclabels
- единственный, который я использовал лично.
Этот вопрос StackOverflow похож, и некоторые ответы на него также могут быть вам полезны.
Это действительно частный случай более общий вопрос функций в разные модули, у которых одинаковые имя, но действовать по другому, несвязанные типы.
Для решения этой проблемы обычно достаточно квалифицированного импорта и псевдонимов.
Вы можете рассмотреть класс типов, если есть общая функция доступа для всех этих типов. Например,
class Fieldable a where
field :: a -> b
instance Fieldable (a,b) where
field = fst
И т.д.
Как опытные разработчики 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
, а не просто режим
].)
Вы можете перегрузить имена классами типов, но опытные разработчики, которых я знаю, не любят ненужные классы типов. Я никогда не могу понять, когда они думают, что типовой класс будет бесплатным или нет.
Я надеюсь, что однажды люди, работающие с Haskell, придут к соглашению с иерархическим пространством имен и начнут использовать уточненные имена. Как задумал Бог.