Haskell:按类型过滤异构列表

16

我们可以使用对序列在 Haskell 中创建异构列表:

type a *: b = (a, b)
a *: b = (a, b)
infixr 5 *:

hlist :: Int *: String *: Maybe Float *: ()
hlist = 1 *: "hello" *: Just 3 *: () -- (1, ("hello", (Just 3, ())))

有没有一种方法可以在这些列表上执行类型级过滤?也就是,定义一些多态函数hfilter,使得对于不同的类型abc

<code>hfilter :: a *: b *: c *: a *: b *: a *: () ->  a *: a *: a *: ()
hfilter :: a *: b *: c *: a *: b *: a *: () ->  b *: b *: ()
hfilter :: a *: b *: c *: a *: b *: a *: () ->  c *: ()
hfilter :: a *: b *: c *: a *: b *: a *: () ->  ()
</code>
2个回答

17

只需进行几个类型扩展即可实现(顺便提一句,请在发布问题时检查您的示例代码是否编译通过。我不得不做出很多更正)。

{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE OverlappingInstances #-}

type a :* b = (a, b)
a *: b = (a, b)
infixr 5 *:
infixr 5 :*

hlist :: Int :* String :* Int :* Maybe Float :* ()
hlist = 1 *: "hello" *: 2 *: Just 3 *: ()


class TypeFilter lst t where
    hfilter :: lst -> [t]

instance TypeFilter () t where
    hfilter _ = []

instance TypeFilter rest t => TypeFilter (t :* rest) t where
    hfilter (a, rest) = a : hfilter rest

instance TypeFilter rest t => TypeFilter (a :* rest) t where
    hfilter (_, rest) = hfilter rest

现在我们可以通过明确定义所需列表的类型来按类型过滤项目。
*Main> hfilter hlist :: [Int]
[1,2]
*Main> hfilter hlist :: [String]
["hello"]
*Main> hfilter hlist :: [Maybe Float]
[Just 3.0]
*Main> hfilter hlist :: [Maybe Int]
[]

这个程序通过定义一个多参数类型类 TypeFilter 来实现,它需要接收包含不同类型的列表的类型以及我们想要过滤的类型。然后我们为空列表/单元 (),类型匹配的列表 (TypeFilter (t :* rest) t) 和头部类型与要检索的类型不同的列表 (TypeFilter (a :* rest) t)) 分别定义实例。

请注意,在最后一个实例中,目前没有办法表示 at 必须是不同的类型,但当它们相同时,OverlappingInstances 会将实例 TypeFilter (t :* rest) t 计算为更具体的实例,并选择它而不是 TypeFilter (a :* rest) t


抱歉编译出现问题,我是用手机发帖的。 - rampion
1
好的,我已经成功地从这个版本中得到了一个不需要 OverlappingInstances 的版本,通过传递一个过滤器参数,并且使用异构列表作为输出。所以 hfilter :: a -> h -> h',其中 hfilter (undefined :: Int) hlist :: ()()hfilter (undefined :: Int) hlist :: Int :* ()1 :* (),而 hfilter (undefined :: Int) hlist :: Int :* Int :* ()1 :* 2 :* () - rampion
但这需要使用OverlappingInstances才能实际使用。 - rampion

2

虽然有一些方法可以实现你所要求的功能,但很可能这并不是 Haskell 的强项。你能详细说明一下你的需求吗?通常你可以在一个代数数据类型中枚举所有需要的变体。这样你的列表就会是同质的,可以使用模式匹配来操作它的元素。


使用ADT,您可能会得到与catMaybes类似的解决方案,该解决方案通过特定构造函数过滤列表。 - Dan Burton
我正在尝试用Haskell编写基于堆栈的DSL(有点类似于Factor)- 在这种情况下,异构列表就是堆栈。 - rampion
1
啊,我想我明白了,也许可以参考Sami的基于SNOC对的多态栈思路在这里描述 - Paul R

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接