Haskell中作为数据类型的构造函数

Haskell中作为数据类型的构造函数,haskell,Haskell,目前我有以下数据类型: data NumberColumn = NumberColumn String [Double] data StringColumn = StringColumn String [String] data UnknownColumn = UnknownColumn String [String] 所有这些数据类型(也有其他数据类型,这些只是域示例)都为csv文件列建模。它们可以表示普通数字、姓名、货币、简单文本等 我想要实现的是这样的目标: data Column =

目前我有以下数据类型:

data NumberColumn = NumberColumn String [Double]
data StringColumn = StringColumn String [String]
data UnknownColumn = UnknownColumn String [String]
所有这些数据类型(也有其他数据类型,这些只是域示例)都为csv文件列建模。它们可以表示普通数字、姓名、货币、简单文本等

我想要实现的是这样的目标:

data Column = NumberColumn String [Double] | StringColumn String [String] | UnknownColumn String [String]
也就是说,我想将它们放在一个数据类型中,这样就可以映射、过滤和创建新项,如下所示:

sumColumn :: NumberColumn -> NumberColumn -> NumberColumn
sumColumn...
问题在于NumberColumn不是一个数据类型,而是一个构造函数,所以我能想到的最好方法是接受并返回列类型:

sumColumn :: Column -> Column -> Column
sumColumn (NumberColumn...) (NumberColumn...)...
这是可行的,但是函数应该只包含NumberColumns的明确性已经丢失,我非常希望保留它


这可以实现吗?

您可以将
NumberColumn
构造函数的数据分解成新的数据类型,从而:

data Column
    = NumberColumn  NumCol
    | StringColumn  String [String]
    | UnknownColumn String [String]

data NumCol = NumCol String [Double]
然后,
sumColumn
仅在
numcoln
s上定义,而不是
Column
s或
NumberColumn
s:

sumColumn :: NumCol -> NumCol -> NumCol
sumColumn (NumCol s1 d1) (NumCol s2 d2) = ...
编辑:

如果希望
numcoll
s的行为类似于
NumberColumn
s,可以使用类型类:

class Columnlike a where
    toColumn :: a -> Column

instance Columnlike Column where
    toColumn = id

instance Columnlike NumCol where
    toColumn = NumberColumn
有了这个类型类,您的
上的函数现在可以覆盖一些
列,比如a
,并且您可以互换地使用
numcoll
s和
s。例如:

colFunction :: Column -> Column
colFunction = ...
变成

colFunction :: Columnlike a => a -> a
colFunction = ...

然后,您可以在
NumCol
s和
NumberColumn
s上使用
colFunction

您可以将
NumberColumn
构造函数的数据分解成新的数据类型,从而:

data Column
    = NumberColumn  NumCol
    | StringColumn  String [String]
    | UnknownColumn String [String]

data NumCol = NumCol String [Double]
然后,
sumColumn
仅在
numcoln
s上定义,而不是
Column
s或
NumberColumn
s:

sumColumn :: NumCol -> NumCol -> NumCol
sumColumn (NumCol s1 d1) (NumCol s2 d2) = ...
编辑:

如果希望
numcoll
s的行为类似于
NumberColumn
s,可以使用类型类:

class Columnlike a where
    toColumn :: a -> Column

instance Columnlike Column where
    toColumn = id

instance Columnlike NumCol where
    toColumn = NumberColumn
有了这个类型类,您的
上的函数现在可以覆盖一些
列,比如a
,并且您可以互换地使用
numcoll
s和
s。例如:

colFunction :: Column -> Column
colFunction = ...
变成

colFunction :: Columnlike a => a -> a
colFunction = ...

然后,您可以在
NumCol
s和
NumberColumn
s上使用
colFunction

似乎需要一个定义为

data Column a = Column String [a]
然后

要将
StringColumn
UnknownColumn
与原始字符串区分开来,请为
Unknown
使用一个新类型,以将其与“普通”字符串区分开来


似乎您需要一个单一类型的构造函数
定义为

data Column a = Column String [a]
然后

要将
StringColumn
UnknownColumn
与原始字符串区分开来,请为
Unknown
使用一个新类型,以将其与“普通”字符串区分开来


除了切普纳的

data Column a = Column String [a]
您可以将
sumColumn
定义为

sumColumn :: Num a => Column a -> Column a -> Column a
sumColumn (Column name1 ms) (Column name2 ns) =
  Column (name1 ++ "+" ++ name2) (zipWith (+) ms ns)
您还可以使用GADT来确保包含数字的列仅按以下方式使用:

{-# LANGUAGE GADTs #-}

data Column a where
  NumColumn :: Num a => String -> [a] -> Column a
  StrColumn :: String -> [String] -> Column String

但是,除了chepner的函数外,还必须约束在
numa=>列a
s上运行的每个函数。

data Column a = Column String [a]
您可以将
sumColumn
定义为

sumColumn :: Num a => Column a -> Column a -> Column a
sumColumn (Column name1 ms) (Column name2 ns) =
  Column (name1 ++ "+" ++ name2) (zipWith (+) ms ns)
您还可以使用GADT来确保包含数字的列仅按以下方式使用:

{-# LANGUAGE GADTs #-}

data Column a where
  NumColumn :: Num a => String -> [a] -> Column a
  StrColumn :: String -> [String] -> Column String

但是,您仍然必须约束在
numa=>列a
s上运行的每个函数。

这将起作用,但这是一个可读性问题。NumCol和NumberColumn是/代表同一事物,因此我非常希望有一个单一的名称。@PetrasPurlys那里可能没有免费的午餐。但是,您可以使
NumCol
NumberColumn
的行为类似,请参见我的编辑。另一种选择是在成对的
(字符串,[Double])
上定义
sumColumn
,但这也有它的缺点。这会起作用,但这是一个可读性问题。NumCol和NumberColumn是/代表同一事物,因此我非常希望有一个单一的名称。@PetrasPurlys那里可能没有免费的午餐。但是,您可以使
NumCol
NumberColumn
的行为类似,请参见我的编辑。另一个选项是对
(字符串,[Double])
定义
sumColumn
,但这也有它的缺点。如果我遗漏了一些东西,我很抱歉,但这怎么能让我保留不同列的列表呢?从这个问题上看,这不是一个明显的要求。@PetraSpurly不幸的是,这两个功能是不兼容的:要么类型系统区分这两个东西,因此,您可以编写一个只接受数字列的函数,或者类型系统不区分这两项,因此您可以将它们都放在列表中。如果您想要两种功能,您需要两种(集合)类型,一种是可分辨的,另一种是不可分辨的。@PetrasPurlys您可以将
a
移到右侧,然后重新获得所需的电源<代码>数据列=全部a。ToColumn a=>Column a(但随后您将丢失并跟踪类型签名中的
a
)如果我遗漏了什么,我很抱歉,但是这怎么能让我保留一个不同列的列表呢?从这个问题上看,这不是一个明显的要求。@PetrasPurlys不幸的是,这两个功能不兼容:要么类型系统区分这两个东西,所以你可以编写一个只接受数字列的函数,或者,类型系统无法区分这两件事,因此可以将它们都放在列表中。如果您想要两种功能,您需要两种(集合)类型,一种是可分辨的,另一种是不可分辨的。@PetrasPurlys您可以将
a
移到右侧,然后重新获得所需的电源<代码>数据列=全部a。ToColumn a=>Column a(但随后您将丢失并跟踪类型签名中的
a