![](/img/trans.png)
[英]Haskell: typeclass and instance (Not in scope: data constructor..)
[英]Haskell Data field as an instance of a typeclass
我想表達一個帶有字段sType的數據類型,它是SType實例的任何東西(來自zeromq-haskell)。 SType是zeromq套接字類型。 在zeromq-haskell源代碼中,有一個例子:
data Pair = Pair
instance SType Pair where
zmqSocketType = const pair
這就是我現在所擁有的
data SocketOpts = SocketOpts
{ end :: SocketEnd
, sType :: SType st => st
}
但是當我像socket ctx $ sType so
使用它時socket ctx $ sType so
我得到:
Ambiguous type variable `a0' in the constraint:
(SType a0) arising from a use of `sType'
(套接字的簽名是socket :: SType a => Context -> a -> IO (Socket a)
當我嘗試在ghci中創建一個SocketOpts時,我得到:
let so = SocketOpts (Bind "some") Pull
<interactive>:1:35:
Could not deduce (st ~ Pull)
from the context (SType st)
bound by a type expected by the context: SType st => st
at <interactive>:1:10-38
`st' is a rigid type variable bound by
a type expected by the context: SType st => st
at <interactive>:1:10
In the second argument of `SocketOpts', namely `Pull'
In the expression: SocketOpts (Bind "some") Pull
In an equation for `so': so = SocketOpts (Bind "some") Pull
我從中了解到,SType比我所要求的更通用(拉,這是SType的實例)。 我應該在這里表達我想要什么?
編輯
這個:
data SocketOpts st = SocketOpts
{ end :: SocketEnd
, sType :: st
}
與以下一起使用:
zmqSource :: (ResourceIO m, SType st) => Context -> SocketOpts st -> Source m a
zmqSource ctx so = sourceIO
mkSocket
recvSock
(\x -> undefined)
where
recvSock = undefined
mkSocket = socket ctx $ sType so
似乎工作,但我會留下問題,以防有更優雅的方式來做到這一點?
編輯2
伙計們,非常感謝你的回答。 根據您的反饋,我現在得到以下信息(由於在github上更容易閱讀,因此我不會在此處發布)
https://github.com/boothead/zeromq-conduit/blob/master/Data/Conduit/ZMQ.hs
我使用GADT(我認為)來嘗試表示設置普通套接字和Sub套接字之間的區別,但是目前存在一些麻煩:我可以使用SockOpts類型來設置子套接字訂閱將不會被調用,它將無法正常工作:
SockOpts (Connect "tcp://127.0.0.1:9999") Sub -- This would be bad
無論如何,有沒有讓類型系統禁止這樣做? 像是尖括號中的東西?
SockOpts :: (SType st, <not SubsType st>) => SocketEnd -> st -> SocketOpts st
我想表達一個帶有字段sType的數據類型,它是SType實例的任何東西(來自zeromq-haskell)。
您在這里所說的“任何東西”含糊不清。
您是否只想使用參數類型創建SocketOpts
值,並在使用SocketOpts
值時強制要求SType
實例? 然后,正常的參數化類型就可以了,就像在編輯中一樣。
是否要確保任何SocketOpts
值都具有指定類型的實例,在創建值而不是使用時強制執行約束? 然后,GADT定義將起作用:
data SocketOpts st where
SocketOpts :: (SType st) => SocketEnd -> st -> SocketOpts st
是否要使用任何實例創建 SocketOpts
值,而不是按特定類型對其進行參數化? 這是一種存在主義類型將起作用:
data SocketOpts where
SocketOpts :: (SType st) => SocketEnd -> st -> SocketOpts
...但是請注意,存在可能很尷尬,並且你可以用這樣的東西做的就是在SocketOpts
上進行模式匹配,然后使用為SType
定義的方法。 缺少對特定類型的了解。
或者,最后,您是否要在使用 SocketOpts
值時選擇任何實例,放棄創建具有特定類型的實例的能力? 這是一種普遍量化的類型:
data SocketOpts where
SocketOpts :: SocketEnd -> (forall st. SType st => st) -> SocketOpts
...我相信你原來的版本是什么。 在這種情況下,您不能使用單態值來創建SocketOpts
值,只能使用多態值 - 考慮Int
值11和數值文字11 :: Num a => a
之間的差異。 請注意,在這種情況下,GHC仍需要知道要選擇哪個實例,因此在使用SocketOpts
的內容時,在使用任何SType
方法之前,您需要特定的類型。
您看到的錯誤都來自上述關於普遍量化類型的警告:“模糊類型”,因為您應用了需要選擇SType
實例的內容而沒有給GHC足夠的信息來執行此操作,並且“無法推斷”錯誤,因為您試圖構造一個具有單形值的SocketOpts
。
我懷疑存在主義版本是你想要的。 但是,除非您有令人信服的理由混合不同的SType
實例, SType
參數化GADT可能是更好的選擇。
你有GADT嗎?
data SocketOpts where
SocketOpts :: SType st => SocketEnd -> st -> SocketOpts
您也可以嘗試使用存在的GADT:
data SocketOpts where
SocketOpts :: SocketEnd -> (forall st . SType st => st) -> SocketOpts
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.