Haskell中Data.List和Data.Sequence的最小接口,用于可加载列表

时间:2015-11-11 15:48:36

标签: haskell

我发现我正在编写重复的函数来处理Data.List和Data.Sequence,并试图对它们进行概括,我写道:

import qualified Data.Sequence as Seq

class PrependableList pl where
  empty :: pl a
  first ::  pl a -> a
  prepend :: a -> pl a -> pl a
  len :: pl a -> Int

instance PrependableList [a] where
  empty = []
  first = head
  prepend = (:)
  len = length

instance PrependableList (Seq.Seq a) where
  empty = Seq.empty
  first seq = Seq.index seq 0
  prepend = (Seq.<|)
  len = Seq.length

上面的实现没有编译,表明种类不匹配:

Kind mis-match
The first argument of `PrependableList' should have kind `* -> *',
but `[a]' has kind `*'
In the instance declaration for `PrependableList [a]'

Kind mis-match
The first argument of `PrependableList' should have kind `* -> *',
but `Seq.Seq a' has kind `*'
In the instance declaration for `PrependableList (Seq.Seq a)'

根据我在其他条目中看到的内容(例如In Haskell, why isn't there a TypeClass for things that can act like lists?),可能无法完全概括多个类似列表的数据结构。

但是,至少对于Data.List和Data.Sequence来说,上述4个函数是否可以推广?

2 个答案:

答案 0 :(得分:2)

感谢大家,这是正确的代码,只有最少的使用示例:

double

打印哪些:

    import qualified Data.Sequence as Seq

    class PrependableList pl where
        empty :: pl a
        first ::  pl a -> a
        prepend :: a -> pl a -> pl a
        len :: pl a -> Int

    instance PrependableList [] where
        empty = []
        first = head
        prepend = (:)
        len = length

    instance PrependableList Seq.Seq where
        empty = Seq.empty
        first seq = Seq.index seq 0
        prepend = (Seq.<|)
        len = Seq.length

    -- Example usage
    prependAll :: (PrependableList pl) => pl a -> [a] -> pl a
    prependAll plist xs = foldr prepend plist xs

    main = do
        print $ prependAll Seq.empty [1..5]
        print $ prependAll [] [1..5]

答案 1 :(得分:2)

我知道你已经为自己回答了这个问题,但我想指出Haskell近年来一直在不断发展,尝试更广泛地封装这些想法,而你的课程已经是另外两个现有课程的组合: / p>

import Control.Monad (MonadPlus, mzero, mplus);
import Data.Foldable (Foldable, length, toList);
import Data.Sequence (Seq)
import qualified Data.Sequence as S

-- same as your PrependableList
class Prependable p where
    empty :: p a
    first ::  p a -> a
    prepend :: a -> p a -> p a
    len :: p a -> Int

-- generic newtype wrapper
newtype WrapP p x = WrapP { unwrapP :: p x } deriving (Show, Eq, Ord)

-- any foldable monadplus is prependable.
instance (MonadPlus m, Foldable m) => Prependable (WrapP m) where
    empty = WrapP mzero
    prepend = (WrapP .) . (. unwrapP) . mplus . return
    first = head . toList . unwrapP
    len = length . unwrapP 

-- specific cases
type SeqP x = WrapP Seq x
type ListP x = WrapP [] x

此外,由于WrapPnewtype,您通常可以将WrapPunwrapP替换为id,只要您还调整类型签名;特别是我们可以将你的四个函数直接编写为:

empty   :: (Foldable m, MonadPlus m) => m x
empty = mzero
first   :: (Foldable m, MonadPlus m) => m x -> x
first = head . toList
prepend :: (Foldable m, MonadPlus m) => x -> m x -> m x
prepend = mplus . return
len     :: (Foldable m, MonadPlus m) => m x -> Int
len = length

这些现在本身将适用于列表和Data.Sequence。 (如果Prelude.lengthData.Foldable.length不同,您可能需要更新版本的GHC,否则您必须导入隐藏隐藏length,然后从数据中明确导入。可折叠的。

由于Data.Sequence已经为MonadPlus定义了FoldableSeq,所以您可以使用8行代码,而不需要新的类或实例声明;此外,其中两个是直接替换(lenlengthemptymzero),如果您只使用已定义的版本,则再保存4行代码你。

(稍微绕道而来,first也是来自extract的{​​{1}},其中一个(非空!)列表的Control.Comonadextract } headduplicate(使用takeWhile (not . null) . tails但忽略了最后一个元素Data.List.tails)。我认为你不能定义[]为基础纯粹在len基础架构上;为此你真的需要一个Comonad类型。非空列表可以通过将其写为Foldable显示在一个明显的comonad表单中,我们在那里然后可以将newtype NEList = NEList x (Maybe (NEList x))替换为NEList以查看它是否为comonad。)