Ответ 1
Как уже отмечалось, это невозможно, но я хотел бы сказать пару о предлагаемых решениях:
Если два поля четко различаются, вы всегда будете знать, что вы используете. Под "явным различием" здесь я имею в виду, что никогда не было бы обстоятельств, когда было бы целесообразно делать то же самое с любой областью. Учитывая это, излишняя двусмысленность на самом деле не является нежелательной, поэтому вам нужен либо квалифицированный импорт в качестве стандартного подхода, либо расширение полезна неоднозначности, если это больше по вашему вкусу. Или, как очень упрощенный (и слегка уродливый) вариант, просто вручную префикс полей, например. deviceArrayName
вместо name
.
Если два поля в некотором смысле одно и то же, имеет смысл уметь относиться к ним однородным образом; в идеале вы можете написать функцию полиморфную в поле выбора name
. В этом случае один из вариантов использует класс типа для "именованных вещей" с функциями, которые позволяют вам получить доступ к полю name
для любого подходящего типа. Главным недостатком здесь, помимо распространения тривиальных ограничений типа и возможных головных болей от Ограниченного Ограничения Мономорфизма, является то, что вы также теряете способность использовать синтаксис записи, который начинает побеждать всю точку.
Другим важным вариантом для подобных полей, который я еще не видел, является извлечение поля name
в один параметризованный тип, например. data Named a = Named { name :: String, item :: a }
. Сам GHC использует этот подход для расположения источников в деревьях синтаксиса, и, хотя он не использует синтаксис записи, идея одинаков. Недостатком здесь является то, что если у вас есть Named DeviceArray
, доступ к полю bytes
теперь требует прохождения двух уровней записей. Если вы хотите обновить поле bytes
с помощью функции, вы застряли в чем-то вроде этого:
addBytes b na = na { item = (item na) { bytes = b + bytes (item na) } }
Тьфу. Есть способы немного смягчить проблему, но они все еще не идеи, на мой взгляд. Случаи, подобные этому, почему мне не нравится синтаксис записи вообще. Итак, в качестве окончательного варианта, магия Template Haskell и пакет fclabels
:
{-# LANGUAGE TemplateHaskell #-}
import Control.Category
import Data.Record.Label
data Named a = Named
{ _name :: String,
_namedItem :: a }
deriving (Eq, Show, Data, Typeable)
data DeviceArray = DeviceArray { _bytes :: Int }
deriving (Eq, Show, Data, Typeable)
data MakefileParams = MakefileParams { _makefileParams :: [MakeParam] }
deriving (Eq, Show, Data, Typeable)
data MakeParam = MakeParam { paramText :: String }
deriving (Eq, Show, Data, Typeable)
$(mkLabels [''Named, ''DeviceArray, ''MakefileParams, ''MakeParam])
Не обращайте внимания на бизнес MakeParam
, мне просто нужно поле, чтобы что-то сделать. В любом случае, теперь вы можете изменять такие поля:
addBytes b = modL (namedItem >>> bytes) (b +)
nubParams = modL (namedItem >>> makefileParams) nub
Вы также можете назвать bytes
что-то вроде bytesInternal
, а затем экспортировать аксессор bytes = namedItem >>> bytesInternal
, если хотите.