2017-09-04 87 views
8

考慮以下GHCI會議:`coerce`和類型變量的實例

>:set -XTypeApplications 
>import Data.Map.Strict 
>import GHC.Exts 
>newtype MySet a = MySet (Map a()) 
>let member' :: Ord a => a -> MySet a -> Bool; member' = coerce member 

<interactive>:21:57: error: 
    * Couldn't match representation of type `a0' with that of `()' 
     arising from a use of `coerce' 
    * In the expression: coerce member 
     In an equation for member': member' = coerce member 
>let member' :: Ord a => a -> MySet a -> Bool; member' = coerce (member @_ @()) 

我怎麼在這裏上的一種預感:類型檢查程序需要滿足Coercible (Ord a => a -> Map a b -> Bool) (Ord a => a -> MySet a -> Bool),不能夠實例b在這個約束到()

有沒有比-XTypeApplications更優雅的方式做到這一點?

編輯:我特別尋找解決方案,處理MySet a的類型,例如union :: Ord a => MySet a -> MySet a -> MySet a許多發生。

+1

'member'ks = member k(coerce s)''怎麼樣? –

+0

好的想法,但是對於'union'(其中'MySet'在類型中多次出現)而言,這是非常詳細的,並且根本不可能用於'fromList'。 –

+2

我認爲這可能是GHC中的一個相當大的錯誤,當然值得一票。或者在牛津與理查德交談:-) –

回答

2
member :: Ord a => a -> Map a b -> Bool 
member' :: Ord a => a -> MySet a -> Bool 

GHC需要接受

Coercible (Map a b) (MySet a) 

它看到

Coercible (MySet a) (Map a()) 

留下它需要

Coercible (Map a()) (Map a b) 

導致

Coercible() b 

但什麼是b?它含糊不清。在這種情況下,b是什麼並不重要,因爲通過參數性,member不可能關心。因此,選擇b ~()並輕鬆解決脅迫是完全合理的。但GHC一般不會在類型推斷中執行這樣的參數分析。我懷疑改變它可能會很棘手。最特別的是,任何時候類型推斷「猜測」,都有可能猜測錯誤的風險,並在其他地方阻止推理。這是一大堆蠕蟲。

至於你的問題,我沒有一個好的解決方案。當你有幾個相似模式的函數時,你可以將它們抽象出來,但是你仍然會面臨很大的煩惱。

+0

聽起來有點像我的直覺告訴我的,但我不確定。謝謝!使用'-XTypeApplications'非常繁瑣,但它工作:https://github.com/sgraf812/pomaps/blob/6d077d3413c3fcc0de05955c2d6b9bd82c085a85/library/Data/Poset/Internal.hs –

+0

順便說一句,我認爲第三和第四個「強制切換。並不是說它改變了這個想法。 –

+1

@SebastianGraf,如果你包裝一個完整的模塊,你會發現使用完整類型簽名比類型應用更容易。 'foo = coerce(M.foo :: theoldthing this that):: forall this that。這是新事物)'。以這種方式複製/粘貼更多,而不用考慮哪些類型參數在哪裏。 – dfeuer

1

TypeApplications與該解決方案是相當簡單:

{-# LANGUAGE TypeApplications #-} 

import Data.Coerce 
import qualified Data.Map as M 

newtype Set a = Set (M.Map a()) 

member :: Ord a => a -> Set a -> Bool 
member = coerce (M.member @_ @()) 

union :: Ord a => Set a -> Set a -> Set a 
union = coerce (M.union @_ @()) 

注意,某些功能將需要更多或更少的通配符,例如

smap :: (Ord b) => (a -> b) -> Set a -> Set b 
smap = coerce (M.mapKeys @_ @_ @()) 

要確定您必須究竟是如何指定類型的應用程序(除了試驗和錯誤),使用

>:set -fprint-explicit-foralls 
>:i M.mapKeys 
M.mapKeys :: 
    forall k2 k1 a. Ord k2 => (k1 -> k2) -> M.Map k1 a -> M.Map k2 a 

:i得到變量順序是TypeApplications使用的相同。

請注意,您不能使用fromListcoerce - 這不是一種限制,它只是沒有任何意義:

fromList :: Ord a => [a] -> Set a 
fromList = coerce (M.fromList @_ @()) 

這給了錯誤

* Couldn't match representation of type `a' with that of `(a,())' 

最好你可以在這裏做的可能是

fromList :: Ord a => [a] -> Set a 
fromList = coerce (M.fromList @_ @()) . map (flip (,)()) 
+1

注意:OP明確表示他們知道TypeApplication解決方案,但我認爲它非常簡單 - 所以這個答案本質上是「不,沒有更優雅的方式」。但是,這看起來主要是基於意見的,所以相反,我將答案本身稱爲一種(簡單!)方法,可以用這種方法機械地編寫這樣的代碼,而不用考慮如何去做。 – user2407038

+1

':set -fprint-explicit-foralls'聽起來就像我在實現https://github.com/sgraf812/pomaps/blob/6d077d3413c3fcc0de05955c2d6b9bd82c085a85/library/Data/Poset/Internal.hs時知道的那樣。 。感謝您的提示,但我認爲其他帖子是對問題的更直接回答。 –