2014-10-03 78 views
2

我工作的圖書館查詢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

雖然不完全是一個答案,我的建議是不要如果他們無法遵守任何財產或法律,請使用類似「GoogleMapsRequest」或「GoogleMaps」的類。看看你的例子,似乎只是定義一些類型並在其上運行一些功能應該可以解決你的問題。 – Sibi 2014-10-03 20:46:47

+0

'class GoogleMapsRequest a b'不允許編譯器從輸入類型推導出結果類型。您可能想要啓用函數依賴關係,並且需要'class GoogleMapsRequest a b | a - > b' – user2407038 2014-10-04 00:46:47

+0

儘管@Sibi提到的並不是真正需要的類型類,但是在使用多參數類型類時,函數依賴關係並沒有做到這一點。謝謝! – 2014-10-04 08:40:09

回答

2

如果我瞭解你,你想有一個功能queryAPI :: a -> M b其中類型ab是相關的。你想有一個單獨的類型之間的「映射」列表。換句話說:

queryAPI :: MapsRequest -> M MapsResponse 
queryAPI :: GeocodeRequest -> M GeocodeResponse 
... 

有辦法做到這一點。但是他們會讓你的用戶感到困惑。特別是Haddock不會指出每個重載函數的實際類型。

你能簡單地爲每個API不同的功能?

queryMaps :: MapsRequest -> M MapsResponse 
queryGeocode :: GeocodeRequest -> M GeocodeResponse 
... 

換句話說,完全拋棄了類型類。包括我在內的許多人不鼓勵使用類型類來簡單地重載函數名稱。 (人們談論「法律」之類的Functor法律;也有像FromJSON,你需要使用類型類來處理遞歸類型的情況。)

編輯:正如你正確地指出,這主要是關於點用戶界面,即導出的API。你如何實施它取決於你;包裝通用queryAPI功能是慣用的。沒有太多有關於它的思想,你的使用情況似乎並沒有哭出來的類型類中實現,而是使用,正如你所指出的,通用的數據類型在內部(「stringly類型化」,爲雙關語去; queryAPI可能是ApiRequest -> M ByteString),然後導出更強類型的公共API。

+0

感謝您的回答。你理解我是對的。你的觀點聽起來很合理,特別是對於用戶來說這可能有點令人困惑。但是,您怎麼看待僅在內部使用類型類併爲每個API導出不同的函數,而這些函數只包裝'queryAPI'?這是一種不好的做法嗎? – 2014-10-04 08:51:57

+0

我將所有內容都改爲使用通用數據類型而不是類型類。似乎是一個更好的方法。 – 2014-10-04 15:06:32

+0

使用內部類型類只要它實際上可以幫助你和公共接口隱藏它,就沒有問題。你的情況是你有一定數量的代碼來建立一個連接,認證等,但實際上並不涉及數據本身?如果是這樣,使用'ByteString's或者其他什麼來「弱類型化」核心函數是完全有效的,然後用更具體類型的單個函數來包裝它。有點像讀取文件(通用數據)與解析文件(通用數據到特定類型)。 – 2014-10-06 17:22:29

0

在GHC中,多參數類型類或相關的(類型/數據)族將按照您的要求進行操作。如果您決定不使用類型類,那麼您仍然可以使用(類型/數據)族。

對於你這樣做MPTCs:

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 
+1

隨着你的第一個解決方案,推理工作將需要fundeps。第二個解決方案有一些問題。我希望'鍵入Response'是一個錯誤,但它不是;你不能只寫一個實例......(它應該是'Response req'。)另外,'queryApi'幾乎肯定需要是類的成員,因爲在嘗試實際使用時會變得很清楚在其實施中響應請求; 'queryApi'不能對自己的類型變量'req'進行案例分析。 – 2014-10-06 17:52:03

+0

@ChristianConkle修復了一些問題;我自己並不經常使用GHC功能,所以我有點驚訝,我的答案和我一樣接近。我主要將'queryApi'移出TC以表明它可以完成。但是,無論是queryApi還是其他一些「低級」函數(例如'getResponseRaw :: req - > IO ByteString')都需要在TC中。 – 2014-10-06 23:42:03