【问题标题】:Haskell Data field as an instance of a typeclassHaskell 数据字段作为类型类的实例
【发布时间】:2011-12-30 03:11:05
【问题描述】:

我想将带有字段 sType 的 Data 类型表示为任何 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的签名是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 比我所要求的更通用(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

【问题讨论】:

    标签: haskell zeromq


    【解决方案1】:

    我想将带有字段 sType 的 Data 类型表示为任何 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 =&gt; a 之间的区别。请注意,在这种情况下,GHC 仍然需要知道要选择哪个实例,因此在使用 SocketOpts 的内容时,您需要在使用任何 SType 方法之前使用特定类型。

    您看到的错误都来自上述关于普遍量化类型的警告:“模糊类型”,因为您应用了一些需要选择 SType 实例的东西,而没有为 GHC 提供足够的信息来执行此操作,并且“不能推断”错误,因为您尝试使用单态值构造 SocketOpts

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

    【讨论】:

      【解决方案2】:

      您是否使用了 GADT?

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

      您也可以尝试使用存在主义的 GADT:

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

      【讨论】:

        猜你喜欢
        • 2022-01-13
        • 2016-08-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-06
        相关资源
        最近更新 更多