Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell pattern matching on vectors

Is it possible to use list style pattern matching on vectors?

ie

import qualified Data.Vector as V

f :: V.Vector a -> a
f (x:xs) = x 

gives an error

like image 733
matt Avatar asked May 03 '16 00:05

matt


3 Answers

-XViewPatterns can let you do this:

{-# LANGUAGE ViewPatterns #-}
module VecViewPats where

import Data.Vector (Vector)
import qualified Data.Vector as V

uncons :: Vector a -> Maybe (a, Vector a)
uncons v = if V.null v
  then Nothing
  else Just (V.unsafeHead v, V.unsafeTail v)

vsum :: Num a => Vector a -> a
vsum (uncons -> Just (a,av)) = a + vsum av
vsum (uncons -> Nothing) = 0

Or -XLambdaCase

import Control.Category ((>>>))
-- ...
vsum :: Num a => Vector a -> a
vsum = uncons >>> \case
  Just (a,av) -> a + vsum av
  Nothing     -> 0

But honestly, that's seems like a bit of a code smell as you're using one data structure (Vector) as another ([]) which suggests that maybe your choice of data structure is off.

If you really just want to treat it like a list for the purposes of some algorithm, why not use toList?

like image 63
rampion Avatar answered Oct 28 '22 10:10

rampion


@Cactus has pointed out -XPatternSynonym (introduced in 7.8) combined with -XViewPattern can be used to pattern match on vectors. I am here to extend his comment a bit further.

pattern Empty <- (V.null -> True) 

The above defines a pattern synonym Empty for the empty vector. The Empty pattern matches against an empty vector using view pattern (V.null -> True). However, it cannot be used as an expression elsewhere, i.e. a uni-directional synonym, since the system doesn't really know what Empty is as a Vector (we only know that null v is True but there could be other vectors giving True as well).

To remedy this, a where clause can be added specifying that Empty actually is a empty vector, i.e. a bi-directional synonym, as well as a type signature:

pattern Empty :: Vector a
pattern Empty <- (V.null -> True) where Empty = V.empty 

This pattern synonym can be used to define uncons without an if expression:

uncons :: Vector a -> Maybe (a, Vector a)
uncons Empty = Nothing
uncons v     = Just (unsafeHead v, unsafeTail v)

We use uncons to define uni-directional synonym. Note I don't make it bi-directional since cons is costly for vector:

pattern (:<|)  :: a -> Vector a -> Vector a
pattern x :<| xs <- (uncons -> Just (x, xs))

Anyway, we are finally able to pattern match on vectors just like lists:

vsum :: Num a => Vector a -> a 
vsum Empty = 0
vsum (x :<| xs) = x + vsum xs

A complete code is here.

like image 28
L.-T. Chen Avatar answered Oct 28 '22 12:10

L.-T. Chen


Vectors aren't intended for that kind of pattern matching--they were created to give Haskell O(1) lists, or lists that can be accessed from any point efficiently.

The closest thing to what you wrote would be something like this:

f v = V.head v

Or, if recursion is what you are looking for, the tail function will get the rest of the list.

But if you are trying to do something that moves along a list like that, there are Vector equivalents of functions such as foldl, find, map, and the like. It depends on what you intend to do.

like image 24
Wolfe Macfarlane Avatar answered Oct 28 '22 12:10

Wolfe Macfarlane