2011-12-29 84 views
1

我想将字段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)

当我尝试创建一个SocketOpt S IN 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 

我从此明白的是,S型是更普遍的是我自找的(拉,这是S型的实例)。我应该如何表达我想要的东西?


编辑

此:

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 

回答

0

您是否支持GADT?

data SocketOpts where 
    SocketOpts :: SType st => SocketEnd -> st -> SocketOpts 

您也可以尝试用GADTs existentials:

data SocketOpts where 
    SocketOpts :: SocketEnd -> (forall st . SType st => st) -> SocketOpts 
3

我想表达一个场S型数据类型是什么,是S型的实例(从zeromq-哈斯克尔)。

这里有什么意思是“什么”有些含糊不清。

是否只想创建参数类型为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 

...但警告说,existentials可劲儿,所有你可以做的东西像这样的是SocketOpts模式匹配,然后使用SType定义的方法。有关特定类型的知识会丢失。

或者,最后,你要选择的任何实例时使用一个SocketOpts,放弃建立一个与特定类型的能力吗?这是一个普遍量化的类型:

data SocketOpts where 
    SocketOpts :: SocketEnd -> (forall st. SType st => st) -> SocketOpts 

......我相信这是你的原始版本。在这种情况下,不能使用单形值创建值,只能创建一个多态值 - 请考虑Int值11与数字字面值11 :: Num a => a之间的差异。需要警告的是,在这种情况下,GHC仍然需要知道选择哪个实例,因此在使用SocketOpts的内容时,在使用任何SType方法之前,您需要特定的类型。

您看到约通用量化类型的上述警告俩来到该错误:“暧昧型”,因为你加的东西,需要选择一个SType实例没有给GHC足够的信息,这样做,而“不能推断“错误,因为你试图构造一个具有单形值的SocketOpts

我怀疑存在的版本是你想要的。但是,除非您有充分的理由混合SType的不同实例,否则参数化GADT可能是更好的选择。