我发现我正在编写重复的函数来处理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个函数是否可以推广?
答案 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
此外,由于WrapP
是newtype
,您通常可以将WrapP
和unwrapP
替换为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.length
与Data.Foldable.length
不同,您可能需要更新版本的GHC,否则您必须导入隐藏隐藏length
,然后从数据中明确导入。可折叠的。
由于Data.Sequence
已经为MonadPlus
定义了Foldable
和Seq
,所以您可以使用8行代码,而不需要新的类或实例声明;此外,其中两个是直接替换(len
为length
,empty
为mzero
),如果您只使用已定义的版本,则再保存4行代码你。
(稍微绕道而来,first
也是来自extract
的{{1}},其中一个(非空!)列表的Control.Comonad
为extract
} head
为duplicate
(使用takeWhile (not . null) . tails
但忽略了最后一个元素Data.List.tails
)。我认为你不能定义[]
为基础纯粹在len
基础架构上;为此你真的需要一个Comonad
类型。非空列表可以通过将其写为Foldable
显示在一个明显的comonad表单中,我们在那里然后可以将newtype NEList = NEList x (Maybe (NEList x))
替换为NEList
以查看它是否为comonad。)