I'm a Haskell newbie, and having a bit of trouble figuring out how to pattern match a ByteString
. The [Char]
version of my function looks like:
dropAB :: String -> String dropAB [] = [] dropAB (x:[]) = x:[] dropAB (x:y:xs) = if x=='a' && y=='b' then dropAB xs else x:(dropAB $ y:xs)
As expected, this filters out all occurrences of "ab" from a string. However, I have problems trying to apply this to a ByteString
.
The naive version
dropR :: BS.ByteString -> BS.ByteString dropR [] = [] dropR (x:[]) = [x] <...>
yields
Couldn't match expected type `BS.ByteString' against inferred type `[a]' In the pattern: [] In the definition of `dropR': dropR [] = []
[]
is clearly the culprit, as it is for a regular String
not a ByteString
. Subbing in BS.empty
seems like the right thing but gives "Qualified name in the binding position: BS.empty." Leaving us to try
dropR :: BS.ByteString -> BS.ByteString dropR empty = empty dropR (x cons empty) = x cons empty <...>
this gives "parse error in pattern" for (x cons empty)
. I don't really know what else I can do here.
As a side note, what I'm trying to do with this function is to filter out a specific UTF16 character from some text. If there's a clean way to accomplish that, I'd love to hear it, but this pattern matching error seems like something that a newbie haskeller should really understand.
Overview. We use pattern matching in Haskell to simplify our codes by identifying specific types of expression. We can also use if-else as an alternative to pattern matching. Pattern matching can also be seen as a kind of dynamic polymorphism where, based on the parameter list, different methods can be executed.
In a functional language, pattern matching involves checking an argument against different forms. A simple example involves recursively defined operations on lists. I will use OCaml to explain pattern matching since it's my functional language of choice, but the concepts are the same in F# and Haskell, AFAIK.
You can use view patterns for such things
{-# LANGUAGE ViewPatterns #-} import Data.ByteString (ByteString, cons, uncons, singleton, empty) import Data.ByteString.Internal (c2w) dropR :: ByteString -> ByteString dropR (uncons -> Nothing) = empty dropR (uncons -> Just (x,uncons -> Nothing)) = singleton x dropR (uncons -> Just (x,uncons -> Just(y,xs))) = if x == c2w 'a' && y == c2w 'b' then dropR xs else cons x (dropR $ cons y xs)
The latest version of GHC (7.8) has a feature called pattern synonyms which can be added to gawi's example:
{-# LANGUAGE ViewPatterns, PatternSynonyms #-} import Data.ByteString (ByteString, cons, uncons, singleton, empty) import Data.ByteString.Internal (c2w) infixr 5 :< pattern b :< bs <- (uncons -> Just (b, bs)) pattern Empty <- (uncons -> Nothing) dropR :: ByteString -> ByteString dropR Empty = empty dropR (x :< Empty) = singleton x dropR (x :< y :< xs) | x == c2w 'a' && y == c2w 'b' = dropR xs | otherwise = cons x (dropR (cons y xs))
Going further you can abstract this to work on any type class (this will look nicer when/if we get associated pattern synonyms). The pattern definitions stay the same:
{-# LANGUAGE ViewPatterns, PatternSynonyms, TypeFamilies #-} import qualified Data.ByteString as BS import Data.ByteString (ByteString, singleton) import Data.ByteString.Internal (c2w) import Data.Word class ListLike l where type Elem l empty :: l uncons :: l -> Maybe (Elem l, l) cons :: Elem l -> l -> l instance ListLike ByteString where type Elem ByteString = Word8 empty = BS.empty uncons = BS.uncons cons = BS.cons instance ListLike [a] where type Elem [a] = a empty = [] uncons [] = Nothing uncons (x:xs) = Just (x, xs) cons = (:)
in which case dropR
can work on both [Word8]
and ByteString
:
-- dropR :: [Word8] -> [Word8] -- dropR :: ByteString -> ByteString dropR :: (ListLike l, Elem l ~ Word8) => l -> l dropR Empty = empty dropR (x :< Empty) = cons x empty dropR (x :< y :< xs) | x == c2w 'a' && y == c2w 'b' = dropR xs | otherwise = cons x (dropR (cons y xs))
And for the hell of it:
import Data.ByteString.Internal (w2c) infixr 5 :• pattern b :• bs <- (w2c -> b) :< bs dropR :: (ListLike l, Elem l ~ Word8) => l -> l dropR Empty = empty dropR (x :< Empty) = cons x empty dropR ('a' :• 'b' :• xs) = dropR xs dropR (x :< y :< xs) = cons x (dropR (cons y xs))
You can see more on my post on pattern synonyms.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With