你给你的要点三种数据类型:(!我同意)
data Media = Media {
mediaId :: Int
, mediaName :: Text
, mediaFilePath :: FilePath
, mediaMimeType :: Text
, mediaHash :: Text
, mediaWidth :: Int
, mediaHeight :: Int
, mediaCreated :: UTCTime
, mediaUpdated :: UTCTime
}
data Media2 = Media2 {
mediaId :: Int
, mediaName :: Text
, mediaFilePath :: Maybe FilePath
, mediaMimeType :: Text
, mediaHash :: Text
, mediaWidth :: Int
, mediaHeight :: Int
, mediaCreated :: UTCTime
, mediaUpdated :: UTCTime
}
data Media3= Media3 {
media3Id :: Int
, media3Name :: Text
, media3FilePath :: FilePath
, media3NewFilePath :: Maybe FilePath
, media3MimeType :: Text
, media3Hash :: Text
, media3Width :: Int
, media3Height :: Int
, media3Created :: UTCTime
, media3Updated :: UTCTime
}
...并抱怨说,这些违反了DRY原则。一个简单的解决方案是拆分共享部分,因此:
data Metadata = Metadata
{ id :: Int
, name :: Text
, mimeType :: Text
, hash :: Text
, width :: Int
, height :: Int
, created :: UTCTime
, updated :: UTCTime
}
然后,您有几个选项可用于参数化其余位。一种选择是使用类型修饰符;例如:
data Located a = Located { location :: FilePath, locatedValue :: a }
data Motion a = Motion { oldLocation, newLocation :: FilePath, motionValue :: a }
data UILocated a = UILocated { uiField :: Maybe FilePath, uilocatedValue :: a }
让老Media
类型,例如,现在会是一个Located Metadata
。另一种选择是有位置的总和类型:
data Location
= OnDisk FilePath
| Nowhere
| Moving FilePath FilePath
那么你可以使用(Metadata, Location)
为你的类型为所有三个,或者把位置的Metadata
场。这会失去一些静态检查,但在某些情况下可能会很方便。
还有第三种选择是一个多态字段添加到元数据类型:
data Metadata a = Metadata
{ id :: Int
, name :: Text
, mimeType :: Text
, hash :: Text
, width :: Int
, height :: Int
, created :: UTCTime
, updated :: UTCTime
, extra :: a
}
让你的旧Media
类型,例如,现在会是Metadata FilePath
,并Media3
将Metadata (FilePath, Maybe FilePath)
。
谢谢,但...我的问题比这更复杂。 在许多其他领域,有些将会是Maybe,有些不会 - 在程序的不同阶段。我试图避免为每个可能的变体创建许多近克隆。 – Simon
@Simon你可以尝试[点菜数据类型](http://www.cs.nott.ac.uk/~wss/Publications/DataTypesALaCarte.pdf)的方法。 – bheklilr
@Simon我担心你需要一个参数很多的类型,它实际上是一个元组(可能是另一个名字)。 – chi