2015-07-20 74 views
2

我正在嘗試擴展(或試圖找出是否有可能擴展)帶有類型簽名的函數,該類型簽名已經達到我的知識範圍,因爲我是庫使用哪些暴露相當多態的類型和API,更多的多態。rankntypes:非法多態或限定類型

我使用的是persistenthsqml,這兩個庫在時刻都有漂亮的毛茸茸的類型,至少對我而言。

我需要將persistent實體包裝爲hsqml「代理」對象纔會暴露給QML(基本上是javascript)。 爲此,我讓自己成爲幫手,叫做getStandardClassMembers。您可以使用它,像這樣:

instance DefaultClass (Entity Project) where 
    classMembers = getStandardClassMembers 
     [ 
      ("name", projectName), 
      ("hasCustomIcon", text . not . BS.null . projectIcon), 
      ("hasDev", projectHasDev), -- TODO bool as string, ugly.. 
      ("hasUat", projectHasUat), 
      ("hasStaging", projectHasStage), 
      ("hasProd", projectHasProd) 
     ] 
     [] 

getStandardClassMembers定義是there。它使用我指定的成員定義了一個javascript對象,並調用我爲實現JS對象的成員而提供的haskell函數。

這很不錯,但有一個'但'。該對中第二個位置的功能必須返回Text。在這裏你看到,除了第一個功能外,我應該返回一個Bool。因此,理想情況下,我會讓getStandardClassMembers的類型簽名採用更多形式的類型,以便不要求第二個函數必須返回Text。我再一次不知道這是否可能,但我決定試一試。

因此,我將RankNTypes添加到已經很大的該文件的語言擴展名列表中(由於我選擇的庫很強大,我認爲這很好)。

我改變:

getStandardClassMembers :: (Marshal tr, ToBackendKey SqlBackend record, Typeable record, 
    MarshalMode tr ICanReturnTo() ~ Yes) => 
    [(String, record -> tr)] -> [(String, ObjRef (Entity record) -> Maybe Int)] 
    -> [Member (GetObjType (ObjRef (Entity record)))] 

要:

getStandardClassMembers :: (Marshal tr, ToBackendKey SqlBackend record, Typeable record, 
    MarshalMode tr ICanReturnTo() ~ Yes) => 
    [(String, forall tr. record -> tr)] -> [(String, ObjRef (Entity record) -> Maybe Int)] 
    -> [Member (GetObjType (ObjRef (Entity record)))] 

(所以我加了forall tr.) ,我得到:

Illegal polymorphic or qualified type: forall tr. record -> tr 
Perhaps you intended to use ImpredicativeTypes 

現在我知道足夠多的這些事情要知道啓用ImpredicativeTypes不是一個好主意。另外,如果我啓用它,它也不起作用。

是我想要達到的目標嗎?還是應該讓它休息一下,並且感謝所有那些已經非常雜亂的類型簽名和語言擴展集合?

編輯可能是正確的解決方法是忘記我的幫手,並回到基本使用hsqml基本API。但除此之外,我仍然好奇這是否可行。

回答

1

我認爲最基本的問題是你試圖把不同類型的值放在一個列表中。即使ImpredicativeTypes也不能真正讓你以你需要的方式做到這一點。存在類型可以使用包裝器,但我認爲它們也是矯枉過正的。

相反,我建議從插入元組更改爲插入總是相同類型的東西,這取決於你實際上將如何使用元組。看你的鏈接代碼,我看到

\(name, f) -> defPropertyConst name (return . f . entityVal . fromObjRef) 

你用你的元組,以獲得功能你其實需要從他們。那麼爲什麼不爲此定義一個全局函數呢?

stdMember name f = defPropertyConst name (return . f . entityVal . fromObjRef) 

(這個名字僅僅是一個建議,你可能想要的東西更短,甚至運營商)。

然後換getStandardClassMembers採取這樣的值的列表作爲第一個參數,而可能是類似的東西第二。

假設沒有更多類型的微妙之處,那麼你應該能夠編寫

instance DefaultClass (Entity Project) where 
    classMembers = getStandardClassMembers 
     [ 
      stdMember "name" projectName, 
      stdMember "hasCustomIcon" $ text . not . BS.null . projectIcon, 
      stdMember "hasDev" projectHasDev, -- TODO bool as string, ugly.. 
      stdMember "hasUat" projectHasUat, 
      stdMember "hasStaging" projectHasStage, 
      stdMember "hasProd" projectHasProd 
     ] 
     [] 
+0

感謝小費如何格式化代碼,我會用「迴歸基本」審查(共同我提到的方法)。但是我沒有完全得到的是,列表中的類型是否真的不同? 'forall'正是試圖統一類型。所有人都喜歡'福爾。記錄 - > a'或某種形狀。也許在返回類型上使用'forall'更麻煩?我仍然不明白爲什麼'RankNTypes'方法不能削減它。 –

+1

啊我現在看到它。這就是你所說的「存在類型可以與封裝一起工作」。就是這樣。所以這是不可行的,除了一個包裝,如在https://wiki.haskell.org/Heterogenous_collections#Existential_types-我知道,但不知何故它沒有「連接」。好吧,我今晚會檢查你的解決方案,記住我的想法,但現在看到只是添加'forall'不能削減它。我想爲什麼會出現在研究論文中,所以就是這樣。 –

+0

@EmmanuelTouzery我不確定在沒有包裝的情況下做存在類型是「真的不可能的」,就像「當包裝使它更簡單時不值得費心去實現」一樣。在[https://wiki.haskell.org/Existential_type](https://wiki.haskell.org/Existential_type)上有一個[Haskell方言]的鏈接(http://www.cs.uu.nl/groups /ST/Projects/ehc/ehc-book.pdf)試圖以不同的方式做到這一點。 –