我正在使用一个库来查询Google Maps Web Services。
我有一个类型
class GoogleMapsRequest a
定义了构建URL和函数所需的一些功能
queryAPI :: (FromJSON x, GoogleMapsRequest r) => r -> GoogleMaps x
(GoogleMaps
是一个简单的MonadStack来获取一些配置值等)。现在我只需要为每个Web服务定义一些数据类型和实例。例如,我创建了地理编码服务:
data GeocodeRequest = GeocodeAddress Text
data GeocodeResponse = ...
instance GoogleMapsRequest GeocodeRequest where
...
geocode :: GeocodeRequest -> GoogleMaps GeocodeResponse
geocode = queryAPI
我的问题是我需要包装queryAPI
或指定显式的响应类型,尽管响应类型基本上是清楚的,我可以做类似的事情:
geocode :: GeocodeRequest -> GoogleMaps Text
编译但总会失败。
所以我的问题就出现了:是否有任何很酷的功能或可能是一个明显的解决方案,我看不到在请求和响应类型之间进行这种映射?
我尝试制作MultiParamTypeClass
:
class GoogleMapsRequest a b
queryAPI :: (FromJSON x, GoogleMapsRequest r x) => r -> GoogleMaps x
但是我不确定这是否是正确的方法,并且我总是从使用该函数来构建网址时得到一些“无法推断”的错误。
谢谢!
答案 0 :(得分:2)
如果我了解您,您希望函数queryAPI :: a -> M b
与a
和b
类型相关。您希望在各个类型之间列出“映射”。换句话说:
queryAPI :: MapsRequest -> M MapsResponse
queryAPI :: GeocodeRequest -> M GeocodeResponse
...
有很多方法可以做到这一点。但它们会让你的用户感到困惑;特别是,Haddock不会指出每个重载函数的实际类型。
您可以为每个API设置不同的功能吗?
queryMaps :: MapsRequest -> M MapsResponse
queryGeocode :: GeocodeRequest -> M GeocodeResponse
...
换句话说,完全抛开类型。很多人,包括我在内,都不鼓励使用类型类来简单地重载函数名。 (人们谈论像Functor
法则那样的“法律”;还有像FromJSON
这样的情况,你需要使用类型类来处理递归类型。)
编辑:正如您正确观察到的,这主要是关于用户界面,即导出的API。你如何实现它取决于你;包装通用queryAPI
函数是惯用的。在没有考虑太多的情况下,你的用例似乎并没有为实现中的类型类而哭泣,而是正如你所指出的那样在内部使用通用数据类型(“stringly-typed”,就像双关语; { {1}}可能是queryAPI
),然后导出更强类型的公共API。
答案 1 :(得分:0)
在GHC中,多参数类型类或关联(类型/数据)系列将按您的要求执行。如果您决定不使用类型类,则可能仍然使用(类型/数据)族。
对于MPTC,您可以执行以下操作:
class GMR req resp | req -> resp where
queryAPI :: req -> GoogleMaps resp
instance GMR GeocodeRequest GeocodeResponse where
...
对于关联类型系列,您可以执行以下操作:
class GoogleMapsRequest req where
type Response req
queryApi :: (GoogleMapsRequest req, FromJSON (Response req)) => req -> GoogleMaps (Response req)
instance GoogleMapsRequest GeocodeRequest where
type Response GoogleMapsRequest = GeocodeResponse