как создать поливариадную функцию haskell?

<p>мне нужна функция, которая принимает произвольное количество аргументов (все одного и того же типа), что-то делает с ними и впоследствии возвращает результат. список аргументов неосуществим в моем конкретном случае.</p><p>просматривая библиотеки haskell, я увидел, что функция <code>printf</code> (из модуля <code>text.printf</code>) использует аналогичный прием. к сожалению, я не мог понять эту магию, посмотрев на источник.</p><p>может кто-нибудь объяснить, как этого добиться, или, по крайней мере, какую-нибудь веб-страницу/бумагу/что-нибудь еще, где я мог бы найти хорошее описание для этого?</p><p><strong>мотивация:</strong></p><p>причина, по которой мне это нужно, очень проста. для школы (урок информатики) мы должны написать модуль, который может "записывать" математическое выражение, выражать его в виде строки (посредством записи экземпляра num/real/и т.д. для собственного типа данных) и выполнять различные операции над ним.</p><p>этот тип данных содержит специальный конструктор для переменной, который может быть заменен значением или чем-то еще указанной функцией. одна из целей - написать функцию, которая принимает такое выражение с некоторым количеством переменных (пары типа <code>(char,rational)</code>) и вычисляет результат выражения. мы должны посмотреть, как лучше выразить цель функции. (моя идея: функция возвращает другую функцию, которая принимает столько же аргументов, сколько и переменных, определенных в функции - кажется невозможным).</p>

<p>ключевым моментом <code>printf</code> является возможность либо вернуть строку, либо функцию. скопировано из <a href="http://www.haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/src/text-printf.html">http://www.haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/src/text-printf.html</a> ,</p><pre><code>printf :: (printftype r) => string -> r printf fmts = spr fmts [] class printftype t where spr :: string -> [uprintf] -> t instance (ischar c) => printftype [c] where spr fmts args = map fromchar (uprintf fmts (reverse args)) instance (printfarg a, printftype r) => printftype (a -> r) where spr fmts args = \a -> spr fmts (touprintf a : args) </code></pre><p>и основная структура, которую мы можем извлечь, это</p><pre><code>variadicfunction :: variadicreturnclass r => requiredargs -> r variadicfunction reqargs = variadicimpl reqargs mempty class variadicreturnclass r where variadicimpl :: requiredargs -> accumulatingtype -> r instance variadicreturnclass actualreturntype where variadicimpl reqargs acc = constructactualresult reqargs acc instance (argclass a, variadicreturnclass r) => variadicreturnclass (a -> r) where variadicimpl reqargs acc = \a -> variadicimpl reqargs (specialize a `mappend` acc) </code></pre><p>например:</p><pre><code>class sumres r where sumof :: integer -> r instance sumres integer where sumof = id instance (integral a, sumres r) => sumres (a -> r) where sumof x = sumof . (x +) . tointeger </code></pre><p>тогда мы могли бы использовать</p><pre><code>*main> sumof 1 :: integer 1 *main> sumof 1 4 7 10 :: integer 22 *main> sumof 1 4 7 10 0 0 :: integer 22 *main> sumof 1 4 7 10 2 5 8 22 :: integer 59 </code></pre>

<p>многие люди говорят вам, как создавать переменные функции, но я думаю, что в этом случае вам действительно лучше просто использовать список типа [(char, rational)].</p>

<p>ответ кенни тм великолепен. ниже приведен пример процесса exec <code>sumof 1 4 7 10 :: integer</code> для лучшей иллюстрации.</p><pre><code>sumof 1 4 7 10 (( \ x -> ( sumof . (x +) . tointeger ) 1 ) 4 7 10 ((sumof . (1 + ) . tointeger) 4 ) 7 10 ( sumof 5 ) 7 10 ( sumof . (5 + ) . tointeger ) 7 10 sumof 12 10 sumof . (12 + ) . tointeger 10 sumof 22 id 22 22 </code></pre>

<p>в вики-статье о функциях с переменными ссылками указывалось <a href="http://okmij.org/ftp/haskell/polyvariadic.html#polyvar-fn">эта статья</a> . я полагаю, это то, что делает printf, но я тоже этого не понимаю. в любом случае, это <em> определенно </em> излишнее, особенно если учесть, что все ваши аргументы одного типа. просто поместите их все в один список. вот для чего хороши списки - произвольное количество вещей одного типа. хорошо, это не очень красиво, но вряд ли будет уродливее полной поливариадной функции.</p>

<p>я посмотрел на <a href="http://okmij.org/ftp/haskell/vararg-fn.lhs">пример</a> ссылку из статьи, на которую ссылался делнан. посмотрев на это немного, я, наконец, понял, что происходит:</p><p>это начинается с этого типа класса:</p><pre><code>class buildlist a r | r-> a where build' :: [a] -> a -> r </code></pre><p>этот бит после канала (|) является функциональной зависимостью. он говорит, что тип, представленный <code>a</code>, может быть определен типом, представленным <code>r</code>. другими словами, вам запрещено определять два экземпляра класса типов <code>buildlist</code> с одинаковым <code>r</code> (тип возвращаемого значения), но с другим <code>a</code>.</p><p>немного забегая вперед, туда, где фактически используется функция <code>build'</code>:</p><pre><code>> build true :: [bool] </code></pre><p>поскольку <code>build</code> просто вызывает <code>build'</code> с пустым списком в качестве первого параметра, это то же самое, что и:</p><pre><code>> build' [] true :: [bool] </code></pre><p>в этом примере <code>build'</code> явно возвращает список. из-за функциональной зависимости мы можем связываться только с этим экземпляром класса типа <code>buildlist</code>:</p><pre><code>instance buildlist a [a] where build' l x = reverse$ x:l </code></pre><p>довольно просто. второй пример более интересный. расширяя определение <code>build</code>, оно становится:</p><pre><code>> build' [] true false :: [bool] </code></pre><p>какой тип <code>build'</code> в этом случае? ну, правила предшествования haskell означают, что вышеприведенное также может быть написано так:</p><pre><code>> (build' [] true) false :: [bool] </code></pre><p>теперь становится ясно, что мы передаем два параметра в <code>build'</code> и затем применяем результат этого выражения к параметру со значением "false". другими словами, ожидается, что выражение <code>(build' [] true)</code> возвратит <em> функцию </em> типа <code>bool -> [bool]</code>. и это связывает нас со вторым экземпляром класса типов <code>buildlist</code>:</p><pre><code>instance buildlist a r => buildlist a (a->r) where build' l x y = build'(x:l) y </code></pre><p>в этом вызове <code>l = []</code> и <code>x = true</code> и <code>y = false</code>, поэтому определение расширяется до <code>build' [true] false :: [bool]</code>. эта подпись привязывается к первому экземпляру <code>build'</code>, и совершенно очевидно, откуда она идет.</p>