2013-08-04 2 views
4

Можно ли сопоставить кортежи с кортежами в Haskell, но не зная размерности кортежа? Я хочу создать функцию, которая Mach против любого кортежа, который первым элементом является A, как:Совпадение по шаблону на основе позиции в Haskell

data A = A Int 
test [email protected](A a,..) = a 

Я знаю, что есть Data.Tuple.Select модуль, и я могу использовать его как это:

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

Но это единственный способ сделать это или у Haskell есть некоторые механизмы по умолчанию, которые можно сопоставить с любым кортежем размера?

ответ

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 
4

Если вы дон Не хотите использовать классы типов, вы также можете использовать вложенные кортежи. Поэтому вместо того, чтобы иметь кортеж типа (A, B, C, D), у вас есть кортеж (A, (B, (C, D))).

Затем можно легко подобрать к первому элементу, однако глубоко вложенного кортежа, как это:

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

Спасибо за это решение! Я буду использовать его. Это не отвечает на мой вопрос (потому что он использует вложенные кортежи), но это очень, очень здорово. –

2

Любое решение будет каким-то образом обобщать над кортежи, как по умолчанию они просто непересекающиеся типы. Наиболее распространенное решение будет использовать typeclasses для индексации по идее типов, которые «имеют первый элемент», например, 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 ... 

хотя при всей своей магии она все еще достигает эту работу через классы типа.

Смежные вопросы