我正在研究绑定到Web服务,其中一些调用采用标志来返回其他信息.例如,通过ID获取艺术家可以使用"录音"标志返回艺术家的录音,并且通过ID获取发行也可以获取录音标记以获得该发行的所有音轨录音.但是,在获取艺术家时,您可以获得该艺术家的所有版本,但是当您实际获得特定版本时,此标志无效.
因此要将其编码到Haskell中,以下内容应该是有效的程序:
getArtistById 5678 [ WithRecordings ]
getReleaseById 37837 [ WithRecordings ]
Run Code Online (Sandbox Code Playgroud)
以下应该是一个无效的程序,并且无法构建:
getReleaseById 739 [ WithReleases ]
Run Code Online (Sandbox Code Playgroud)
我想到了一些解决方案,但我不确定应该采取哪些措施.浮现在脑海的第一个念头是使用类型类ArtistFlag和ReleaseFlag,但是这并不几个原因是有意义的.首先,ArtistFlag f => [f]表示同一个标志的列表,这是有缺陷的.而且,类型类意味着将来添加额外标志的能力,这也没有多大意义 - 有一定数量的标志.
我的下一个选项是每个端点标志的单独数据声明:
data ArtistFlag = ArtistWithRecordings | ArtistWithReleases
data ReleaseFlag = ReleaseWithRecordings
Run Code Online (Sandbox Code Playgroud)
这有点笨拙 - 理想情况下*WithRecordings应始终使用相同的名称,以简化程序员的API.
最后,这是我由于缺乏知识而没有探索过的唯一选择,这可能是HList可以解决的.getArtistById应该采用一组异类的艺术家旗帜.我不知道如何在HList中表达这个,或者即使它可以做到这一点.
很想听到打字大师不得不说:)
我正在实现的API的实际部分位于http://musicbrainz.org/doc/XML_Web_Service/Version_2#Subqueries - 注意recordings标志等.
从字面上理解你的问题,这不能用一个简单的列表来实现; 列表中的每个元素都具有完全相同的类型,并且独立于所有其他元素,因此不可能像这样"远距离行动".
然而,要使它在非常小的情况下工作所需的更改(实际上,需要更多它才能使它与HList一样工作).这是一个让您的API保持基本相同但不需要任何前缀的想法:使用type-classes来表示多种类型支持的标志.
data ArtistFlag = ArtistWithRecordings | ArtistWithReleases
data ReleaseFlag = ReleaseWithRecordings
withReleases :: ArtistFlag
withReleases = ArtistWithReleases
class HasRecordingsFlag flag where
withRecordings :: flag
instance HasRecordingsFlag ArtistFlag where
withRecordings = ArtistWithRecordings
instance HasRecordingsFlag ReleaseFlag where
withRecordings = ReleaseWithRecordings
getArtistById :: Int -> [ArtistFlag] -> IO (Maybe Artist)
getReleaseById :: Int -> [ReleaseFlag] -> IO (Maybe Release)
Run Code Online (Sandbox Code Playgroud)
对用户代码的唯一更改是With小写.这是一个简单的解决方案,可能是解决问题的最简单,最惯用的方法,特别是考虑到您正在连接的外部API的限制.
但是,在我看来,在某些情况下,这可能是重新调整API的好机会; 例如,getArtistById并且getReleaseById你已经显示功能让我担心.如果getArtistById 42 [withReleases]类型相同getArtistById 42 [],则没有静态保证您实际从前一个调用中获取了版本; Maybe [Release]结果中可能有一个字段或类似字体,并且一个请求发布的艺术家的程序将不得不不安全地解包Maybe(例如fromJust),因为API中的不变量未在类型系统中编码.
对此最好的解决方案可能取决于各种因素过于局部化和细节化,但需要考虑的事情.对我来说另外一件事是,据推测,"艺术家的所有录音"与"艺术家所有发行的所有录音"都是一样的.因此,实质上,与艺术家一起检索版本是一种优化 ; 如果我们能够表达更"原始"的形式,并以最有效的方式自动检索,那将是最好的.
当然,这可能并不总是可以实现,如果您尝试创建直接API,甚至可能都不可取.但它是理想的,并告诉我,如果你愿意使用类型系统尽可能地获得最好的API,那么可能值得考虑改变焦点:)