Проблемы с рефакторингом текущих типов (возможно, связанных с GADT/Type Families)
У меня есть такие типы:
-- There are codes
newtype ICode = ICode { fromICode :: String }
newtype RCode = RCode { fromRCode :: String }
data DCode = DCode1 | DCode2 | DCode3
-- There are locations described by type and code.
-- Current implementation looks like this:
data Location = LocType1 ICode
| LocType2 ICode
| LocType3 RCode
| LocType4 DCode
Я хотел бы провести рефакторинг этих типов для решения некоторых проблем, которые присутствуют в текущей реализации.
С QuickCheck действительно легко продемонстрировать свойства, которые мне нужны Arbitrary
и Эзона FromJSON
экземпляры и еще одна функция. Первые 3 свойства необходимы для генерации правильных тестовых данных и 4-е для реализации бизнес-логики.
Я хотел бы иметь возможность:
сделать произвольные экземпляры всех типов кода, таких как
instance Arbitrary ICode where arbitrary = ... -- same with RCode and DCode
делать
Arbitrary
экземпляры таких типов, какLocation1 ICode
(Это явно отличается от текущей реализации, и это то, что я пытаюсь исправить), которые описывают точное сочетание типа местоположения и типа кода.Location1 ICode
может содержать только подмножествоICode
возможные значения, поэтому я должен убедиться в этом.делать
FromJSON
экземпляры всех возможных типов, что-то в строках:instance FromJSON (Location a) where parseJSON = ...
Необходимо десериализовать некоторые объекты json в зависимости от их значений.
Некоторые функции должны работать только на одном типе местоположения. Это довольно неудобно в текущей реализации, потому что я должен использовать либо неполные функции, либо не совсем корректные возвращаемые типы, такие как
Maybe
, Я хотел бы иметь возможность сделать что-то вроде:location1IncludedInArbitraryLocation :: LocType1 -> Location a -> Bool location1IncludedInArbitraryLocation l = ...
Я полагаю, что решение лежит где-то на территории GADT /Data Families, но я не очень хорошо разбираюсь в этом типе фу. Если возможно несколько способов решить эту проблему, какой из них будет легче проверить / поработать позже?
1 ответ
3 и 4 кажутся несовместимыми. Это звучит как "резервный" механизм: используйте этот экземпляр, если более конкретного экземпляра нет. Вы можете получить это с помощью OverlappingInstances, но у меня всегда возникают проблемы с этим. Думаю, стоит попробовать.
Что касается остальной части вашей проблемы, кажется, что вы хотите Location
быть гадом.
data LocType = Type1 | Type2 | Type3 | Type4
data Location :: LocType -> * where
LocType1 :: ICode -> Location Type1
LocType2 :: ICode -> Location Type2
LocType3 :: RCode -> Location Type3
LocType4 :: DCode -> Location Type4
Тогда вы можете легко сделать:
location1IncludedInArbitraryLocation :: Location Type1 -> Location t -> Bool
location1IncludedInArbitraryLocation (LocType1 icode) l = ...
Никакие другие случаи не должны быть определены здесь, потому что никакой другой конструктор не будет хорошо напечатан.
Надеюсь, этого достаточно, чтобы начать играть.
(потребности DataKinds
, KindSignatures
, GADTs
)