多态返回类型和"刚性类型变量" Haskell中的错误

时间:2016-03-16 22:24:13

标签: haskell vector

有一个简单的记录Column v a,其中包含VectorData.Vectorv,因此Vector.Unboxed可以是Vector,只有{{} 1}}等),它的名称和类型(简单的枚举类似ADT SupportedTypes)。我希望能够使用binary包序列化它。为此,我尝试在下面定义一个Binary实例。

现在put工作正常,但是当我尝试在get函数中定义反序列化并想要根据{{返回的rawVector设置特定类型时1}}(colType当它U.Vector Int64时,PInt当它U.Vector Double等时 - 我收到此错误消息:

  

无法将PDouble类型与v

匹配      

U.Vector是由v处的实例声明绑定的刚性类型变量:75:10

     

预期类型:src/Quark/Base/Column.hs

     

实际类型:v a

错误。

是否有更好的方法来实现我的目标 - 根据U.Vector Int64值反序列化不同类型的Vector,或者我坚持为所有可能的{{1}定义colType实例/原始类型组合?不应该是这样的......

Haskell有点新鲜,感谢任何帮助!谢谢!

Binary

更新感谢您下面的所有答案,一些不错的主意!很清楚,我想做的事情是不可能正面实现的 - 所以这就是我的答案。但其他建议的解决方案本身就是一个很好的解读,感谢一帮!

3 个答案:

答案 0 :(得分:2)

您实际遇到的问题(或者如果您还没有,那就是您将会遇到的问题)是您正在尝试根据输入值确定结果类型。你不能那样做。完全没有。你可以巧妙地将结果类型锁定在一个盒子里并扔掉钥匙,这样外面的类型似乎是正常的,但是你不能用它做任何事情,因为你将这个类型锁在一个盒子里然后扔掉了键的。你可以使用GADT存储关于它的额外信息,并用类型类实例加载它,但即使这仍然不是一个好主意。

如果您只是为Column提供了两个构造函数来反映是否存在Int s或Double s的向量,那么您的生活将变得更加轻松。

但实际上,不要这样做。只需让自动派生的二进制实例为您解决任何可反序列化的值到您的向量中。

data Column a = ... deriving (Binary)

使用DeriveAnyClass扩展程序,您可以派生任何具有Generic实现(Binary具有的)的类。然后,只需在需要时反序列化Column DoubleColumn Int

答案 1 :(得分:2)

您真正想要表达的类型是

data Column v = Column (Either (v Int) (v Double))

但这种表述可能对您不满意。那么如何使用向量本身在构造函数的“顶层”编写此类型?

首先,在类型级别开始表示您的总和(Either Int Double),而不是值级别:

data IsSupportedType a where 
  TInt :: IsSupportedType Int 
  TDouble :: IsSupportedType Double 

从这里Column实际上非常简单:

data Column v a = Column (IsSupportedType a) (v a) 

但您可能希望a存在量化,以便按您的需要使用它:

data Column v = forall a . Column (IsSupportedType a) (v a) 

二进制实例如下:

instance (Binary (v Int), Binary (v Double)) => Binary (Column v) where
  put (Column t v) = do 
    case t of 
      TInt -> put (0 :: Int) >> put v 
      TDouble -> put (1 :: Int) >> put v 

  get = do 
    t :: Int <- get 
    case t of 
      0 -> Column TInt <$> get  
      1 -> Column TDouble <$> get 

请注意,此处Vector没有固有的依赖性 - v可能真的是什么。

答案 2 :(得分:1)

正如评论所说,你可以根本不在案例上,并且总是打电话

vec <- get
return Column {rawVector = vec, colName = nm, colType = pt}

这样可以正确完成您的类型签名。但请注意,colType对您没有用处 - 您无法强制它对应于向量中的类型,因为它只存在于值级别。但这可能没问题,您可能只想从数据结构中删除colType,因为您始终可以直接从a中选择的具体类型Column v a派生它。

事实上,Column类型中的约束也没有做太多好处,我认为最好将其渲染为

data Column v a = Column {rawVector :: v a, colName :: Text}

现在,您可以在必要时在呼叫站点强制执行G.Vector约束......