任何解决方案都必须以某种方式泛化元组,因为默认情况下它们只是不相交的类型。最常见的解决方案将使用类型类来索引“具有第一个元素”的类型的概念,例如 Control.Lens 或 Data.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 ...
尽管它具有魔力,但它仍然通过类型类来完成这项工作。