2014-07-09 16 views
6

我在寫一些數據訪問例程,使用Persistent。我希望我的API可以用代表JSON的數據類型來定義,但在持久性方面,我的數據類型是由持久化的模板系統定義的。如何指定函數的類型,它們在函數的參數中沒有使用?

鑑於我有從json到數據庫數據類型的映射,反之亦然,我以爲我應該能夠編寫通用數據訪問例程。

全部進行得很順利,直到我試圖寫插入功能:

standardInsert :: forall d . forall j . 
        (PersistEntityBackend d ~ SqlBackend, PersistEntity d, SimpleJsonDataAccessConversion j d) 
       => j -> DatabaseEnvironmentT (Maybe (WithId j)) 
standardInsert json = do 
    maybeId <- runSqlMaybe $ insert db 
    return $ toApi <$> maybeId 
    where db  = jsonToDataAccess json :: d -- Scoped type variable here. 
     toApi key = addId key $ dataAccessToJson db 

j是JSON數據類型變量類型,d是持久性數據類型變量類型)。

該函數有兩個類型變量,jd,但只能從參數中推斷出j

換句話說,如果我打電話standardInsert jsonValue,類型變量d是不明確的。

我想把它叫做C++ - standardInsert<FooJsonType, FooPersistentType>(jsonValue)

如何告訴Haskell d是什麼?還是我完全用錯誤的方式去解決這個問題?

回答

9

GHC將無法推斷出類型變量d。您需要通過添加一個僞參數將其添加到類型簽名本身。標準技巧是使用代理作爲這個啞參數,這意味着調用者不需要給出該類型的實際值。

您可以從tagged包獲得Proxy爲GHC < 7.8,或從base爲GHC> = 7.8,但對於解釋的目的,我會在此明確定義它:

data Proxy a = Proxy 

standardInsert :: forall d . forall j . 
        (PersistEntityBackend d ~ SqlBackend, 
        PersistEntity d, SimpleJsonDataAccessConversion j d) 
       => Proxy d -> j -> DatabaseEnvironmentT (Maybe (WithId j)) 
standardInsert _ json = do (...) 

,然後在呼叫地點:

standardInsert (Proxy :: Proxy FooPersistentType) jsonValue 
+2

從GHC 7.8開始,'Data.Proxy'實際上是在'base'中。 – Carl

+1

而不是指定一個函數使用'代理d',我更喜歡使用'p d'的參數。在這種類型的代碼中,已經有一些匹配該類型的東西是相當常見的,所以預先存在的值可以直接使用,我更願意明確寫出'Proxy :: d'。 –

+0

太棒了,非常感謝!我開始認爲我可能已經有了一個類型類型快樂,也許我應該拋棄'SimpleJsonDataAccessConversion jd',並且只傳遞一個包含'j-> d'和'd-> j'方法的數據結構。不過,我很想知道這是否可能。非常感謝您提供非常明確的答案。 – stusmith

相關問題