LINUX.ORG.RU

Generic Haskell


0

0

Доброго времени суток

Вопрос по обобщённому программированию в Haskell (возможно, не только, но возник именно в нём). Задача : обобщить стандартную функцию map на функторы с произвольным количеством аргументов

-- стандартный map
map :: (a -> b) -> [a] -> [b]

-- двухаргументный map
map2 :: (a -> a -> b) -> [a] -> [b]

например реализующий попарное суммирование элементов списка :
map2 (\x y -> x + y) [1, 2, 3] = [3, 5]

Задача : реализовать mapn. Стандартный Haskell (Haskell-98) не позволяет обобщить определение map подобным образом. Generic Haskell позволяет обобщить map на произвольный тип (не только список) :
map :: (a -> b) -> d a -> d b

но вот по количеству аргументов никакой документации у них нет. На #haskell @ FreeNode меня отправили смотреть Template Haskell, но, несмотря на наличие некоторого количества примеров, осиливаю его с трудом

Соответственно и вопрос : сталкивался кто-либо с данной задачей, есть ли мысли о решении, есть ли информация о методах/библиотеках для решения подобных задач. Так же было бы интересно услышать о возможностях решения на других ЯП (например, я могу решить эту задачу на Tcl, хотя и не лучшим образом, и вообще не могу на C++) - очевидно, что на CL она должна решаться элементарно, если кто-то сталкивался с Meta OCaml/Nemerle, было бы интересно услышать и их мнение

Заранее спасибо

★★★★★

ошибка в сообщении : обобщённый map (pmap) не в Generic Haskell, а в PolyP; ну в принципе суть вопроса от этого не меняется

jtootf ★★★★★
() автор топика
Ответ на: комментарий от anonymous

>Зачем это надо?

в моём случае задача возникла в процессе реализации фильтров сигналов (БИХ и КИХ-фильтров). подобное обобщение значительно повысило бы выразительность кода. но мне в данный момент оно больше интересно в принципе, с точки зрения теоретических возможностей языков программирования : мне не хватает гибкости Haskell'а :( и я хочу понять, можно ли (и как) эту проблему побороть

jtootf ★★★★★
() автор топика

Соорудил на скорую руку идиотское решение:

{-# OPTIONS_GHC -fglasgow-exts -fallow-undecidable-instances -fallow-incoherent-instances #-}
module MapN (mapN) where
class MapN f a b where
    mapN' :: f -> [a] -> ([a],[b])
    mapN :: f -> [a] -> [b]
    mapN f xs = snd $ mapN' f xs
instance MapN (a -> b) a b where mapN' f xs = (drop 1 xs, map f xs)
instance MapN f a (a -> b) => MapN f a b where
    mapN' f xs =
        let (xt,fs) = mapN' f xs
        in (drop 1 xt, zipWith id fs xt)

При использовании приходится явно указывать типы для всего:

*MapN> mapN ((\a b c -> a + b + c) :: Integer -> Integer -> Integer -> Integer) [1::Integer,2,3,4,5,6] :: [Integer]
[6,9,12,15]

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

спасибо, интересный вариант :) подозревал, что как-то так сделать можно, но опыта реализовать не хватило, да и с нестандартными расширениями пока не работал. код разберу, сразу только вопрос - что именно здесь относится к "-fglasgow-exts", "-fallow-undecidable-instances", и "-fallow-incoherent-instances" ?

>Соорудил на скорую руку идиотское решение:

а почему идиотское ? единственно, что требует явного указания типов - что, насколько я понимаю, здесь избыточно

jtootf ★★★★★
() автор топика
Ответ на: комментарий от Miguel

Другой вариант, с теми же проблемами:

{-# OPTIONS_GHC -fglasgow-exts -fallow-overlapping-instances #-}
module MapN (mapN) where
class MapN f a b where
    mapN' :: [f] -> [a] -> [b]
mapN :: MapN f a b => f -> [a] -> [b]
mapN = mapN' . repeat
instance MapN (a -> b) a b where mapN' = zipWith id
instance MapN f a b => MapN (a -> f) a b where
    mapN' fs xs =
        let fs' = zipWith id fs xs
            xt = drop 1 xs
        in mapN' fs' xt

Miguel ★★★★★
()
Ответ на: комментарий от jtootf

-fglasgow-exts - включает многопараметрические классы (иначе хаскель ругнётся уже на class MapN f a b - ему подавай только MapN f)

-fallow-undecidable-instances - иначе будет ругаться на instance MapN f a (a -> b) => MapN f a b; фишка в том, что получается потенциально бесконечная цепочка: чтобы понять, имеется ли instance MapN f a b, нужно понять, имеется ли instance MapN f a (a -> b); для этого нужно понять, имеется ли instance MapN f a (a -> a -> b) - и так далее до бесконечности.

-fallow-incoherent-instances - там таки возникает эта самая бесконечная цепочка; на каком-то шаге соответствующий instance находится, но компилятор не может угадать, нет ли другого - в этом случае получится перекрытие и надо сигналить об ошибке. Этот свитч говорит "найдя первый instance, забей на поиски остальных - мамой клянусь, их нету, а если и есть - то там то же самое, а если не то же самое - то разница пофигу."

Идиотское - потому что, например, на mapN ((+ 1) :: Integer -> Integer) [1::Integer,2,3,4,5,6] :: [Float] компилятор имеет полное право зависнуть (ибо ему только что дали проследить ту самую бесконечную цепочку, только на этот раз безуспешно). GHC через некоторое количество итераций остановится.

Ниже я привёл более чистый вариант.

Miguel ★★★★★
()
Ответ на: комментарий от jtootf

You're welcome.

И ещё один вариант (обошёлся только тем, что входит в glasgow-exts):
{-# LANGUAGE MultiParamTypeClasses,FlexibleInstances #-}
module MapN (mapN) where
class MapN f a b where
    mapN' :: [f] -> [a] -> [b]
mapN :: MapN f a b => f -> [a] -> [b]
mapN f xs = repeat f `mapN'` xs
instance MapN b a b where mapN' = const
instance MapN f a b => MapN (a -> f) a b where
    mapN' fs xs =
        let fs' = zipWith id fs xs
            xt = drop 1 xs
        in mapN' fs' xt

Говорила мне мама: считай с нуля, а не с единицы...

Miguel ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.