Я хотел бы выразить тип данных с полем 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
, я получаю:
Ambiguous type variable `a0' in the constraint:
(SType a0) arising from a use of `sType'
(подпись сокета socket :: SType a => Context -> a -> IO (Socket a)
Когда я пытаюсь создать SocketOpts в ghci, я получаю:
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 является более общим, чем то, что я просил (Pull, который является экземпляром 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 (я думаю), чтобы попытаться выразить разницу между настройкой обычного сокета и вспомогательного сокета, но на данный момент есть морщинка: я мог бы использовать тип SockOpts для настройки вспомогательного сокета, в этом случае subscribe не будет вызываться, и это не сработает:
SockOpts (Connect "tcp://127.0.0.1:9999") Sub -- This would be bad
Есть ли способ заставить систему типов запретить это? Что-то вроде того, что у меня в угловых скобках?
SockOpts :: (SType st, <not SubsType st>) => SocketEnd -> st -> SocketOpts st