如何在类型之间创建映射

时间:2014-10-03 20:43:01

标签: haskell

我正在使用一个库来查询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

但是我不确定这是否是正确的方法,并且我总是从使用该函数来构建网址时得到一些“无法推断”的错误。

谢谢!

2 个答案:

答案 0 :(得分:2)

如果我了解您,您希望函数queryAPI :: a -> M bab类型相关。您希望在各个类型之间列出“映射”。换句话说:

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