Haskell 使单个函数在列表、bytestring和文本(可能还有其他类似的表示形式)上工作
我正在写一个函数,它在一系列任意符号中进行搜索。我想让它足够通用,这样它可以在列表上工作,Haskell 使单个函数在列表、bytestring和文本(可能还有其他类似的表示形式)上工作,haskell,text,fold,bytestring,lenses,Haskell,Text,Fold,Bytestring,Lenses,我正在写一个函数,它在一系列任意符号中进行搜索。我想让它足够通用,这样它可以在列表上工作,Foldables以及ByteStrings和Texts。将其推广到可折叠的很简单。但是如何通过testrings和Texts包含?当然,我可以将ByteString转换成一个列表,然后调用我的函数,但是我会失去所有的优势ByteStrings 为了得到一个具体的例子,假设我们想要制作一个直方图函数: import Control.Monad.State import qualified Data.Fold
Foldable
s以及ByteString
s和Text
s。将其推广到可折叠的很简单。但是如何通过testring
s和Text
s包含?当然,我可以将ByteString
转换成一个列表,然后调用我的函数,但是我会失去所有的优势ByteString
s
为了得到一个具体的例子,假设我们想要制作一个直方图函数:
import Control.Monad.State
import qualified Data.Foldable as F
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Word
import qualified Data.ByteString as B
import qualified Data.Text as T
type Histogram a = Map a Int
empty :: (Ord a) => Histogram a
empty = Map.empty
histogramStep :: (Ord a) => a -> Histogram a -> Histogram a
histogramStep k = Map.insertWith (+) k 1
histogram :: (Ord a, F.Foldable t) => t a -> Histogram a
histogram = F.foldl (flip histogramStep) empty
但是,由于ByteString
和文本都不能是可折叠的(它只存储Word8
s/Char
s,而不是任意元素),因此我一直在创建更多与之前完全相同的函数,只是使用不同的类型签名:
histogramBS :: B.ByteString -> Histogram Word8
histogramBS = B.foldl (flip histogramStep) empty
histogramText :: T.Text -> Histogram Char
histogramText = T.foldl (flip histogramStep) empty
这在Haskell这样的函数式语言中是不可能的
如何使其通用,一次性编写直方图?
过了一段时间,我自己提出了一个解决方案,但我不确定是否可以用更好的方法解决,或者是否有人已经在某个库中这样做了
我用TypeFamilies
as创建了一个类型类
class Foldable' t where
type Element t :: *
foldlE :: (b -> Element t -> b) -> b -> t -> b
-- other functions could be copied here from Foldable
和实例:
newtype WrapFoldable f a = WrapFoldable { unwrapFoldable :: f a }
instance (F.Foldable f) => Foldable' (WrapFoldable f a) where
type Element (WrapFoldable f a) = a
foldlE f z = F.foldl f z . unwrapFoldable
instance Foldable' B.ByteString where
type Element B.ByteString = Word8
foldlE = B.foldl
instance Foldable' T.Text where
type Element (T.Text) = Char
foldlE = T.foldl
或者更好地使用FlexibleInstances
:
instance (F.Foldable t) => Foldable' (t a) where
type Element (t a) = a
foldlE = F.foldl
现在我可以写(使用FlexibleContexts
):
并在Foldable
s、ByteString
s、Text
s等上使用它
- 还有其他(可能更简单)方法吗?
- 是否有一些库(以这种或另一种方式)解决此问题?
您的解决方案与软件包的功能非常相似。还有一个附加的包,它添加了代码< >文本< /代码>和<代码>向量< /代码>。 您可以考虑对象化折叠:
{-# LANGUAGE GADTs #-}
import Data.List (foldl', unfoldr)
import qualified Data.ByteString.Lazy as B
import qualified Data.Vector.Unboxed as V
import qualified Data.Text as T
import qualified Data.Map as Map
import Data.Word
type Histogram a = Map.Map a Int
empty :: (Ord a) => Histogram a
empty = Map.empty
histogramStep :: (Ord a) => Histogram a -> a -> Histogram a
histogramStep h k = Map.insertWith (+) k 1 h
histogram :: Ord b => Fold b (Histogram b)
histogram = Fold histogramStep empty id
histogramT :: T.Text -> Histogram Char
histogramT = foldT histogram
histogramB :: B.ByteString -> Histogram Word8
histogramB = foldB histogram
histogramL :: Ord b => [b] -> Histogram b
histogramL = foldL histogram
-- helper library
-- see http://squing.blogspot.fr/2008/11/beautiful-folding.html
-- note existential type
data Fold b c where Fold :: (a -> b -> a) -> !a -> (a -> c) -> Fold b c
instance Functor (Fold b) where fmap f (Fold op x g) = Fold op x (f . g)
foldL :: Fold b c -> [b] -> c
foldL (Fold f x c) bs = c $ (foldl' f x bs)
foldV :: V.Unbox b => Fold b c -> V.Vector b -> c
foldV (Fold f x c) bs = c $ (V.foldl' f x bs)
foldT :: Fold Char t -> T.Text -> t
foldT (Fold f x c) t = c $ (T.foldl' f x t)
foldB :: Fold Word8 t -> B.ByteString -> t
foldB (Fold f x c) t = c $ (B.foldl' f x t)
sum_, product_ :: Num a => Fold a a
sum_ = Fold (+) 0 id
product_ = Fold (*) 1 id
length_ :: Fold a Int
length_ = Fold (const . (+1)) 0 id
maximum_ = Fold max 0 id
我发现了另一个使用package的解决方案,它有一个详细的类型类层次结构来标识不同类型的数据结构。其方法与应用程序的答案中的方法类似-它将折叠客观化:
{-# LANGUAGE RankNTypes #-}
import Control.Monad.State
import qualified Data.Foldable as F
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Word
import qualified Data.ByteString as B
import qualified Data.Text as T
import Control.Lens.Fold
import qualified Data.ByteString.Lens as LBS
import qualified Data.Text.Lens as LT
type Histogram a = Map a Int
empty :: (Ord a) => Histogram a
empty = Map.empty
histogramStep :: (Ord a) => a -> Histogram a -> Histogram a
histogramStep k = Map.insertWith (+) k 1
-- Histogram on anything that can be folded into `a`:
histogram :: (Ord a) => Fold c a -> c -> Histogram a
histogram f = foldlOf f (flip histogramStep) empty
-- Specializations are simple:
histogramF :: (Ord a, F.Foldable t) => t a -> Histogram a
histogramF = histogram folded
histogramBS :: B.ByteString -> Histogram Word8
histogramBS = histogram LBS.bytes
histogramText :: T.Text -> Histogram Char
histogramText = histogram LT.text
你总是问一些有趣的问题,因为你对自己正在做的事情有着深刻的思考,并且总是想了解更多+1虽然我可能不会这样做,但这很有趣。绝对值得探索。我还认为,Fold
是一个comonad:实例comonad(Fold b),其中extract(Fold x r)=rx;重复(Fold f x r)=Fold f x(\y->Fold f y r)
,我简单地检查了一下规则,似乎有效。这可以提供更多的方式来组合其操作;这当然是适用的——正如评论中所指出的,这是拉金讨论它的目的。我忘了提到伟大的科纳尔·埃利奥特(Conal Elliot)的许多帖子,例如《跟随拉金》(Follow Rabkin),我还没有完全读过。他和Rabkin似乎没有对这里争论的事实做太多的解释,Fold
类型与列表无关,但是可以应用于任何串行类型X,只要有一个通用的foldX
函数。我首先从sdcvvc
的评论中了解到它
{-# LANGUAGE RankNTypes #-}
import Control.Monad.State
import qualified Data.Foldable as F
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Word
import qualified Data.ByteString as B
import qualified Data.Text as T
import Control.Lens.Fold
import qualified Data.ByteString.Lens as LBS
import qualified Data.Text.Lens as LT
type Histogram a = Map a Int
empty :: (Ord a) => Histogram a
empty = Map.empty
histogramStep :: (Ord a) => a -> Histogram a -> Histogram a
histogramStep k = Map.insertWith (+) k 1
-- Histogram on anything that can be folded into `a`:
histogram :: (Ord a) => Fold c a -> c -> Histogram a
histogram f = foldlOf f (flip histogramStep) empty
-- Specializations are simple:
histogramF :: (Ord a, F.Foldable t) => t a -> Histogram a
histogramF = histogram folded
histogramBS :: B.ByteString -> Histogram Word8
histogramBS = histogram LBS.bytes
histogramText :: T.Text -> Histogram Char
histogramText = histogram LT.text