基于位置的元组模式匹配在Haskell中

时间:2013-08-04 21:32:51

标签: haskell pattern-matching tuples

是否可以在Haskell中模式匹配元组,但不知道元组的维数?我想创建一个与任何元组相对应的函数,第一个元素是A,如:

data A = A Int
test args@(A a,..) = a

我知道有Data.Tuple.Select模块,我可以像这样使用它:

test args = case sel1 args of
    A a -> a
    ...

但这是否是这样做的唯一方法,或者Haskell是否有一些默认机制来匹配任何维度元组?

3 个答案:

答案 0 :(得分:7)

您可以使用ViewPatterns扩展名模式匹配应用于参数的函数的结果:

{-# LANGUAGE ViewPatterns #-}

data A = A Int
test (fst -> A a) = a

您可以使用镜头投射任意字段:

{-# 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

答案 1 :(得分:4)

如果您不想使用类型类,也可以使用嵌套元组。因此,不是使用(A, B, C, D)类型的元组,而是拥有(A, (B, (C, D)))的元组。

然后,您可以轻松地匹配深度嵌套元组的第一个元素,如下所示:

test :: (A, b) -> Int
test (A a, _) = a

答案 2 :(得分:2)

任何解决方案都必须以某种方式推广元组,因为默认情况下它们只是不相交的类型。最常见的解决方案是使用类型类来索引“具有第一个元素”类型的概念,例如Control.LensData.Tuple.Select

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
...

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)
...

在这两种情况下,考虑你的函数的类型,它必须有一种方法来指定你的第一个参数是“带有第一个元素的东西”。

test :: (Sel1 s A)       => s -> ...
test :: (Field1 s t A b) => s -> ...

你也可以走fixed-vector的路线,并将元组视为短均匀向量。你失去了对异质载体采取行动的能力,但是你获得了整洁的类型(但是丑陋的价值),比如

test :: (Vector v A, Index N1 (Dim v)) => v A -> ...
test v = let (A a) = index (1,2) (undefined :: Z) in ...

虽然它的所有魔力仍然通过类型类实现了这项工作。