重构当前类型时遇到问题(可能 GADT/Type 家族相关)

Trouble refactoring current types(possibly GADT/Type Families related)

我有这样的类型:

-- 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 的 FromJSON 实例以及其他功能可以很容易地演示我所追求的属性。 需要前 3 个属性来生成正确的测试数据,第 4 个属性需要 实施业务逻辑。

我希望能够:

  1. make 所有代码类型的任意实例如

        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. 有些功能只能在一种位置类型上工作。这在当前的实现中非常不方便,因为我必须使用不完整的函数或不正确的 return 类型,例如 Maybe。我希望能够做类似的事情:

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

我相信解决方案就在 GADTs/Data 家庭领域的某个地方,但我对这种打字不是很流利。如果有多种方法可以解决此问题,那么稍后 typecheck/work 哪种方法更容易?

3 和 4 似乎不兼容。这听起来像是一种 "fallback" 机制:如果没有更具体的实例可用,则使用此实例。您可以使用 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 = ...

这里不需要定义其他情况,因为没有其他构造函数是良类型的。

我希望这能让你开始玩。

(需要 DataKindsKindSignaturesGADTs