簡體   English   中英

Haskell數據字段作為類型類的實例

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM