如何在Haskell中模拟这种递归结构?

如何在Haskell中模拟这种递归结构?,haskell,kdb,algebraic-data-types,traversable,Haskell,Kdb,Algebraic Data Types,Traversable,我试图通过Haskell类型的系统对kdb/q“原子和列表”进行建模 在kdb/q中,所有数据都是由原子构建的。原子是特定数据类型的不可约值。Int、boolean和char都是原子的例子。列表是从原子构建的有序集合。由于q是一种向量语言,大多数内置操作都是原子的,因此它会递归到参数结构中,直到到达原子为止 例如: (1;2;3)是整数1、2、3的简单列表 (1.0;2;(3;4;5))是1.0(浮点)、2(整数)和简单整数列表(3;4;5)的通用列表 neg是一个对一个数求反的函数。例如: 负

我试图通过Haskell类型的系统对kdb/q“原子和列表”进行建模

在kdb/q中,所有数据都是由原子构建的。原子是特定数据类型的不可约值。Int、boolean和char都是原子的例子。列表是从原子构建的有序集合。由于q是一种向量语言,大多数内置操作都是原子的,因此它会递归到参数结构中,直到到达原子为止

例如:

(1;2;3)是整数1、2、3的简单列表

(1.0;2;(3;4;5))是1.0(浮点)、2(整数)和简单整数列表(3;4;5)的通用列表

neg是一个对一个数求反的函数。例如:

负1收益率-1

负-1.0产生1f

负(1.0;2;(3;4;5))收益率(-1f;-2;(-3;-4;-5))

这就是促使我尝试用Haskell类型来模拟这种行为的原因。数据类型应由atom类型和列表组成

以下是我到目前为止所做的简化版本。我还进一步尝试使其成为可折叠和可遍历的实例

data Atom = I Int
          | C Char
          | D Double 
          deriving Show

data Q a = QAtom a 
         | QList [Q a]
         deriving Show

instance Functor Q where
    fmap f (QAtom a) = QAtom (f a)
    fmap f (QList qs) = QList $ fmap (fmap f) qs

instance Foldable Q where
    foldMap f (QAtom a) = f a
    foldMap f (QList qs) = mconcat $ fmap (foldMap f) qs

instance Traversable Q where
    sequenceA (QAtom fa) = fmap QAtom fa
    sequenceA (QList []) = pure $ QList []
    sequenceA (QList (qfa:qfas)) = concatL <$> (sequenceA qfa) <*> (sequenceA (QList qfas))
        where
            concatL (QAtom a) (QList qas) = QList ((QAtom a):qas)
数据原子=I Int
|C字符
|D双
衍生节目
数据Q a=QAtom a
|QList[Q a]
衍生节目
实例函子Q,其中
fmap f(QAtom a)=QAtom(f a)
fmap f(QList qs)=QList$fmap(fmap f)qs
实例可折叠Q在哪里
折叠图f(QAtom a)=f a
foldMap f(QList qs)=mconcat$fmap(foldMap f)qs
实例可遍历Q其中
sequenceA(QAtom fa)=fmap QAtom fa
sequenceA(QList[])=纯$QList[]
sequenceA(QList(qfa:qfas))=concatL(sequenceA-qfa)(sequenceA(QList-qfas))
哪里
concatL(QAtom a)(QList qas)=QList((QAtom a):qas)
这就是我所拥有的,它可以编译,但我并不特别喜欢concatL函数,它没有根据类型涵盖所有模式。一旦我开始向Q添加一个新的值构造函数QDict[(qatom,qa)],情况就会变得更糟

我对原始数据的建模正确吗?我是否应该尝试使其可遍历?然而,我认为如果我需要将数据类型与Maybe或两者一起使用来建模错误,那么Traversable是必要的

任何建议都将不胜感激


编辑:编辑的q代码格式

编译器知道如何为您的类型自动派生一个可遍历的实例。如果执行
:set-ddump deriv-dsuppress all-xderivtraversable-XStandaloneDeriving
,然后执行
派生实例可遍历Q
,则可以看到“正确”答案。如果您将这些知识应用到您的实例中,您将得到以下结果:

instance Traversable Q where
    sequenceA (QAtom fa) = fmap QAtom fa
    sequenceA (QList qfas) = fmap QList (traverse sequenceA qfas)
或者如果您希望避免
遍历
,而选择
序列a

instance Traversable Q where
    sequenceA (QAtom fa) = fmap QAtom fa
    sequenceA (QList qfas) = fmap QList (sequenceA (fmap sequenceA qfas))
关键是列表本身是可遍历的,因此您可以对它们调用
sequenceA
,而无需以自己的类型重新包装它们


旁注,在您的
folddmap
实例中,不要链接
mconcat
fmap
,只需再次使用
foldMap
,因为列表也是可折叠的:

instance Foldable Q where
    foldMap f (QAtom a) = f a
    foldMap f (QList qs) = foldMap (foldMap f) qs

初步建议:我将不使用类型变量定义
Q
,如
dataq=QAtom | QList[Q]
。这允许您创建列表,例如
QList[QAtom(d1.0)、QAtom(i2)、QList[QAtom(i3)、QAtom(i4)、QAtom(i5)]
,就像您的Q示例一样;不能用当前类型表示这样的结构。但除此之外,我不知道;我必须多看一看你的代码,看看我是否还能找到其他建议。编辑:我错了,请看下一篇评论事实上,不,没关系:我把你的构造函数
QAtom a
误读为
QAtom(Atom a)
。您当前对
Q
的定义实际上很好!很抱歉造成混淆。至于您的
可遍历的
实例:我认为您肯定应该将其作为
可折叠的
等的实例,但正如您已经注意到的那样,您的
可遍历的
实例有点滑稽。实现这种情况的更好方法是避免模式匹配,而是使用
Traversable[]
实例:
sequenceA(QList fl)=fmap QList$sequenceA$fmap sequenceA fl
。(你可以认为这是对
QList
中的每个元素进行排序,然后对提供给你的整个操作列表进行排序。)但我发现实现
traverse
sequenceA
容易得多。“列表是从原子构建的有序集合”这句话有误导性。这在某种意义上是正确的,因为KDB中的所有类型最终都是从原子构建的,但只有简单的列表是直接从原子构建的。无论如何,为了更好地理解q数据类型在KDB中的表示方式,您可能需要查看C接口的KDB头文件(k.h,可以在code.kx.com上找到)。另一个有用的资源是KDB for Erlang、Java、C#和Python at的接口集合,它们用这些语言表示KDB/q数据类型。我不认为每种类型最多有一个可遍历实例。我可以为
数据对a=数据对a想出至少两个