Como criar uma function haskell polyvariadic?

Eu preciso de uma function que leva um número arbitrário de argumentos (todos do mesmo tipo), faz algo com eles e depois retorna um resultado. Uma lista de argumentos é impraticável no meu caso específico.

Ao examinar as haskell libs, vi que a function printf (do módulo Text.Printf ) usa um truque semelhante. Infelizmente, não consegui entender essa mágica olhando para a fonte.

Alguém pode explicar como conseguir isso, ou pelo menos alguma página / papel / o que quer que eu possa encontrar uma boa descrição para isso?

Motivação:

A razão pela qual eu preciso disso é realmente muito simples. Para a escola (aula de informática), somos obrigados a escrever um módulo que é capaz de “gravar” uma expressão matemática, expressá-la como uma string (Via escrever uma instância de Num / Real / etc para um tipo de dados próprio) e executar várias operações nele.

Esse tipo de dados contém um construtor especial para uma variável, que pode ser substituído por um valor ou o que quer que seja por uma function especificada. Um dos objectives é escrever uma function, que usa essa expressão com algum número de variables ​​(pares de tipo (Char,Rational) ) e calcula o resultado da expressão. Devemos ver como expressar melhor o objective da function. (Minha idéia: A function retorna outra function que leva exatamente tantos argumentos quanto os vars que são definidos na function – parece ser impossível).

Os pontos-chave do printf é a capacidade de retornar um String ou uma function. Copiado de http://www.haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/src/Text-Printf.html ,

 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) 

e a estrutura básica que podemos extrair é

 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) 

Por exemplo:

 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 

então poderíamos usar

 *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 

Muitas pessoas estão dizendo a você como criar funções variadicas, mas eu acho que neste caso você está melhor apenas usando uma lista de tipos [(Char, Rational)].

No artigo wiki sobre funções variadicas, este artigo foi referenciado. Eu suponho que isso é o que o printf faz, mas eu também não entendo. De qualquer forma, isso é certamente um exagero, especialmente porque seus argumentos são todos do mesmo tipo. Basta colocá-los todos em uma lista. É para isso que as listas são boas – um número arbitrário de coisas do mesmo tipo. Tudo bem, não é muito bonito, mas dificilmente será mais feio do que uma function polivariada completa.

Eu dei uma olhada em um exemplo ligado a partir do artigo que delnan referenciado. Depois de olhar para ele um pouco, acho que finalmente compreendo o que está acontecendo:

Começa com este tipo de class:

 class BuildList ar | r-> a where build' :: [a] -> a -> r 

Esse bit depois do pipe (|) é uma dependência funcional. Diz que o tipo representado por a pode ser determinado pelo tipo representado por r . Em outras palavras, você é impedido de definir duas instâncias da BuildList BuildList com o mesmo r (tipo de retorno), mas diferente a .

Saltar à frente um pouco para onde a function build' é realmente usada:

 > build True :: [Bool] 

Como a build está apenas chamando build' com uma lista vazia como o primeiro parâmetro, isso é o mesmo que:

 > build' [] True :: [Bool] 

Neste exemplo, build' está claramente retornando uma lista. Devido à dependência funcional, só podemos nos vincular a essa instância da class de tipo BuildList :

 instance BuildList a [a] where build' lx = reverse$ x:l 

Bem direto. O segundo exemplo é mais interessante. Expandindo a definição de build , torna-se:

 > build' [] True False :: [Bool] 

Qual é o tipo de build' neste caso? Bem, as regras de precedência de Haskell significam que o acima também poderia ser escrito assim:

 > (build' [] True) False :: [Bool] 

Agora fica claro que estamos passando dois parâmetros para build' e então aplicando o resultado dessa expressão a um parâmetro com valor’ False ‘. Em outras palavras, espera-se que a expressão (build' [] True) retorne uma function do tipo Bool -> [Bool] . E isso nos liga à segunda instância da BuildList BuildList:

 instance BuildList ar => BuildList a (a->r) where build' lxy = build'(x:l) y 

Nesta invocação, l = [] e x = True e y = False , então a definição se expande para build' [True] False :: [Bool] . Essa assinatura se liga à primeira instância do build' , e é bastante óbvio para onde vai a partir daí.

A resposta do KennyTM é ótima. Abaixo está um exemplo do processo exec de sumOf 1 4 7 10 :: Integer para dar uma melhor ilustração.

 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