我知道你要求使用基于镜头的方法,但如果你只有少量元组,你可以轻松地通过使用类型类实现你想要的功能。例如,考虑以下内容:
class ZipTuple as a where
type TupOf as x :: *
zipTuple :: (a -> b -> c) -> as -> TupOf as b -> TupOf as c
instance ZipTuple (a,a) a where
type TupOf (a,a) b = (b,b)
zipTuple f (a1,a2) (b1,b2) = (f a1 b1, f a2 b2)
instance ZipTuple (a,a,a) a where
type TupOf (a,a,a) b = (b,b,b)
zipTuple f (a1,a2,a3) (b1,b2,b3) = (f a1 b1, f a2 b2, f a3 b3)
可能有更优雅的编写方式,但模式很直接。您可以轻松添加所需长度元组的实例。
如果你想要任意长度的元组但不想使用模板哈斯克尔,也可以使用泛型方法。以下是一种基于通用表示形状进行zip操作的解决方案:
import GHC.Generics
class TupleZipG fa fb a b c | fa -> a, fb -> b where
type Out fa fb a b c :: (* -> *)
tupleZipG :: (a -> b -> c) -> fa x -> fb x -> Out fa fb a b c x
instance (TupleZipG l1 l2 a b c, TupleZipG r1 r2 a b c) => TupleZipG (l1 :*: r1) (l2 :*: r2) a b c where
type Out (l1 :*: r1) (l2 :*: r2) a b c = Out l1 l2 a b c :*: Out r1 r2 a b c
tupleZipG f (l1 :*: r1) (l2 :*: r2) = tupleZipG f l1 l2 :*: tupleZipG f r1 r2
instance TupleZipG (S1 m (Rec0 a)) (S1 m' (Rec0 b)) a b c where
type Out (S1 m (Rec0 a)) (S1 m' (Rec0 b)) a b c = S1 m (Rec0 c)
tupleZipG f (M1 (K1 a)) (M1 (K1 b)) = M1 $ K1 $ f a b
instance TupleZipG fa fb a b c => TupleZipG (D1 m fa) (D1 m' fb) a b c where
type Out (D1 m fa) (D1 m' fb) a b c = D1 m (Out fa fb a b c)
tupleZipG f (M1 a) (M1 b) = M1 $ tupleZipG f a b
instance TupleZipG fa fb a b c => TupleZipG (C1 m fa) (C1 m' fb) a b c where
type Out (C1 m fa) (C1 m' fb) a b c = C1 m (Out fa fb a b c)
tupleZipG f (M1 a) (M1 b) = M1 $ tupleZipG f a b
tupleZip
:: (TupleZipG (Rep as) (Rep bs) a b c, Generic cs, Generic as,
Generic bs, Out (Rep as) (Rep bs) a b c ~ Rep cs) =>
(a -> b -> c) -> as -> bs -> cs
tupleZip f t1 t2 = to $ tupleZipG f (from t1) (from t2)
警告:使用此通用方法进行类型推断效果不佳。
Vector
呢?它通过其Applicative
实例支持任意数量的zip(并通过通常的zipWith
,zipWith3
等名称支持固定的小数量)。 - Daniel WagnerEach
)来构建此功能的东西都将难以避免你的问题2和3。但是你可以很容易地制作一个类型类解决方案,如果你的元组很小,那么你不需要太多的实例,所以样板代码并不太糟糕。 - Ben