Есть ли что-то вроде `map2 :: (i -> a) -> (i -> b) -> [i] -> [(a,b)]`?

Я просто написал такие функции до map4 только потому, что они кажутся полезными:

map2 :: Functor f => (i -> a) -> (i -> b) -> f i -> f (a,b)
map2 f1 f2 = fmap $ \i -> (f1 i, f2 i)

Прежде чем перейти к map8, я решил спросить, есть ли что-то подобное в каком-нибудь стандартном модуле. Hayoo, кажется, не знает ни одной функции, которая имеет сигнатуру выше.

Примечание. Я уже нашел Control.Arrow.&&&, который сокращает приведенное выше до:

map2 f1 f2 = fmap (f1 &&& f2)

Но, кажется, нет аналогичной функции для разветвления более двух.


person fho    schedule 12.06.2013    source источник


Ответы (2)


(->) i является аппликативным функтором, поэтому вы можете записать (&&&) как

f &&& g = (,) <$> f <*> g

и вы могли бы написать map3 как

map3 f1 f2 f3 = map ((,,) <$> f1 <*> f2 <*> f3)

за исключением того, что он не короче, чем

map3 f1 f2 f3 = map $ \i -> (f1 i, f2 i, f3 i)

Но благодаря подсказке Габриэля это стало короче:

map3 f1 f2 f3 = map (liftA3 (,,) f1 f2 f3)
person Sjoerd Visscher    schedule 12.06.2013
comment
Вы также можете использовать liftA3 (,,) - person Gabriel Gonzalez; 13.06.2013
comment
О, классно! Это определенно лучше (в смысле меньше писать), чем . - person fho; 15.06.2013
comment
О... но liftAn определяется только до n = 3. Теперь было бы неплохо иметь сахар аппликативного синтаксиса от Idris. - person fho; 15.06.2013
comment
@Florian, вы можете использовать для этого SHE: personal.cis.strath. ac.uk/conor.mcbride/pub/she - person Sjoerd Visscher; 15.06.2013
comment
Описание @SjoerdVisscher заставило меня рассмеяться ... но я предпочитаю, чтобы мой Haskell был чистым и подлым :) - person fho; 15.06.2013
comment
Просто наткнулся на этот мой старый вопрос. Просто хотел отметить, что сегодня я предпочитаю синтаксис f <$> a <*> b, а не liftA2. :) - person fho; 16.07.2014

Стандартной функции для разветвления более двух не существует, хотя вы можете смоделировать это с помощью вложенных кортежей:

f :: i -> a
g :: i -> b
h :: i -> c

f &&& g :: i -> (a, b)

(f &&& g) &&& h :: i -> ((a, b), c)

Если вам не нравятся вложенные кортежи, вам придется написать эту функцию самостоятельно:

fanout3 :: (i -> a) -> (i -> b) -> (i -> c) -> i -> (a, b, c)
fanout3 f g h i = (f i, g i, h i)

Как вы упомянули в своем вопросе, если у вас есть такая функция, вы можете просто map ее:

map (fanout3 f g h) :: [i] -> [(a, b, c)]
person Gabriel Gonzalez    schedule 12.06.2013
comment
Кстати... есть что-то вроде класса типов Flatten, который преобразует такие вещи, как (a,(b,c)) в (a, b, c) или [[[a],a],a] в [a,a,a]. Я знаю о concat, но, похоже, там есть общая структура. - person fho; 15.06.2013
comment
@Florian Мне не известно о классе типа Flatten, но даже если бы он был, я бы, вероятно, избегал его. Было бы очень трудно рассуждать о том, когда он перестанет сглаживаться или каким будет окончательный тип. - person Gabriel Gonzalez; 15.06.2013
comment
Я предполагаю, что для списков будет работать некоторое рекурсивное применение concat до тех пор, пока результат больше не изменится. (Разве fix не об этом?) - person fho; 17.06.2013
comment
@Florian fix не будет работать, потому что тип меняется после каждого применения concat. Однако я бы вообще не стал делать что-то подобное, потому что это не очень идиоматический Haskell. Кроме того, это требует большого количества злоупотреблений классом типов и приводит к очень запутанным ошибкам компилятора. - person Gabriel Gonzalez; 17.06.2013
comment
Да... я не хотел его использовать... это было просто из интереса :) - person fho; 17.06.2013