Reputation: 11813
Is it possible to pattern match tuples in Haskell, but without knowing the dimension of tuple? I want to create a function which mach against any tuple, which first element is A
, like:
data A = A Int
test args@(A a,..) = a
I know there is Data.Tuple.Select
module and I can use it like this:
test args = case sel1 args of
A a -> a
...
But is this the only way of doing this or has Haskell got some default mechanisms to match against any dimension tuple?
Upvotes: 4
Views: 429
Reputation: 2582
You could use the ViewPatterns
extension to pattern match the result of a function applied to an argument:
{-# LANGUAGE ViewPatterns #-}
data A = A Int
test (fst -> A a) = a
You could use lenses to project out arbitrary fields:
{-# LANGUAGE ViewPatterns #-}
import Control.Lens
import Control.Arrow ((&&&))
data A = A Int
test (fields _1 _3 -> (A x, A y)) = x + y
fields f1 f2 = (^.f1) &&& (^.f2)
-- > test (A 1, A 2, A 3)
-- > 4
Upvotes: 7
Reputation: 74404
Any solution will have to somehow generalize over tuples as by default they're simply disjoint types. The most common solution will use typeclasses to index on the idea of types which "have a first element" such as what Control.Lens
or Data.Tuple.Select
does.
class Sel1 a b | a -> b where sel1 :: a -> b
instance Sel1 (a1,a2) a1 where sel1 (x,_) = x
instance Sel1 (a1,a2,a3) a1 where sel1 (x,_,_) = x
instance Sel1 (a1,a2,a3,a4) a1 where sel1 (x,_,_,_) = x
...
or
instance Field1 (Identity a) (Identity b) a b where
_1 f (Identity a) = Identity <$> indexed f (0 :: Int) a
instance Field1 (a,b) (a',b) a a' where
_1 k ~(a,b) = indexed k (0 :: Int) a <&> \a' -> (a',b)
instance Field1 (a,b,c) (a',b,c) a a' where
_1 k ~(a,b,c) = indexed k (0 :: Int) a <&> \a' -> (a',b,c)
instance Field1 (a,b,c,d) (a',b,c,d) a a' where
_1 k ~(a,b,c,d) = indexed k (0 :: Int) a <&> \a' -> (a',b,c,d)
...
In both cases, consider the type of your function, it'll have to have a way to specify your first argument is a "kind of thing with a first element".
test :: (Sel1 s A) => s -> ...
test :: (Field1 s t A b) => s -> ...
You can also go the route of fixed-vector
and consider tuples as short homogenous vectors. You lose the ability to act on heterogenous vectors, but you gain neat types (but ugly values) like
test :: (Vector v A, Index N1 (Dim v)) => v A -> ...
test v = let (A a) = index (1,2) (undefined :: Z) in ...
though for all its magic it still achieves this work through type classes.
Upvotes: 2
Reputation: 2392
If you don't want to use type classes, you could also use nested tuples. So instead of having a tuple of type (A, B, C, D)
you have a tuple of (A, (B, (C, D)))
.
You could then easily match against the first element of a however deeply nested tuple, like this:
test :: (A, b) -> Int
test (A a, _) = a
Upvotes: 4