2015-01-29 2 views
0

Я получил типов, как это:Trouble рефакторинга текущих типов (возможно GADT/типа семей, связанный)

-- 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 и Aeson's FromJSON экземпляров и еще одна функция. Первые 3 свойства необходимы для генерации правильных тестовых данных, а 4-й - реализуют бизнес-логику.

Я хотел бы быть в состоянии:

  1. делают произвольные экземпляры всех типов кодов, таких как

    instance Arbitrary ICode where 
         arbitrary = ... 
        -- same with RCode and DCode 
    
  2. Arbitrary делают экземпляры типов, как Location1 ICode (Это явно отличается от тока и это то, что я пытаюсь исправить), которые описывают точную комбинацию типа местоположения и типа кода. Location1 ICode может содержать только подмножество ICode возможных значений, поэтому я должен убедиться в этом.

  3. сделать FromJSON экземпляров всех возможных типов, что-то в линиях:

    instance FromJSON (Location a) where 
        parseJSON = ... 
    

    Это необходимо для десериализации некоторых объектов JSon в зависимости от их значений.

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

    location1IncludedInArbitraryLocation :: LocType1 -> Location a -> Bool 
    location1IncludedInArbitraryLocation l = ... 
    

Я считаю, что решение лежит где-то в GADTs/территориях Families данных, но я не очень свободно с таким типом-фу. Если возможно несколько способов решения этой проблемы, какой из них легче будет выполнять с помощью метода typecheck/work?

+0

вы пробовали * типы фантомные * в раздел 'ICode' и т.д.? – phadej

+0

@phadej Я подумал об этом, но еще не пробовал. Кажется, что типы фантомов помогут с произвольными экземплярами, но не помогут с номером свойства 4. – dredozubov

+0

Для 4. Я бы предложил создать собственный тип 'newtype LocTypeOne = LocTypeOne ICode' и' LocType1 LocTypeOne' внутри 'LocType' ADT , Затем добавьте функцию «явного принуждения» LocTypeOne -> LocType', и все готово. – phadej

ответ

2

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

Что касается остальной части вашей проблемы, похоже, вы хотите, чтобы Location был GADT.

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 = ... 

Никаких других случаев здесь, потому что никакого другого конструктор не будет хорошо напечатал.

Надеюсь, это даст вам достаточно, чтобы начать играть.

(Needs DataKinds, KindSignatures, GADTs)

+0

Я получаю ваши аргументы и, может быть, 3 должен идти (мне все еще интересно, если можно сделать все вышеперечисленные свойства). :) Я немного расстроен другим аспектом. Кажется, что 'LocType' должен быть закодирован несколько иначе, чтобы этот код работал так, как он должен был. Однако это отправная точка. – dredozubov

+0

Ах, неважно, вторая часть. Я почему-то думал, что «LocType» - это тип, который будет использоваться вместо типов «Код». Я думаю, что это, наконец, имеет смысл для меня. – dredozubov

+0

@dredozubov, я просто понял, что №3 спрашивает о FromJSON, а не о произвольном. В этом случае это определенно возможно. Странно то, что 'a' в' Location a', который вы читаете, предоставляется вызывающим абонентом, а не JSON, так что может привыкнуть к нему. Вы можете использовать экзистенциальный, чтобы обеспечить его синтаксический анализ. 'data AnyLocation где AnyLocation :: Location a -> AnyLocation', затем определите' FromJSON AnyLocation'. – luqui