Após uma pequena digressão nos tipos básicos, podemos retornar às funções novamente. Em particular, para o enigma mencionado anteriormente: se uma função matemática pode usar apenas um parâmetro, como uma função em F # pode usar mais parâmetros? Mais detalhes sob o corte!

A resposta é bastante simples: uma função com vários parâmetros é reescrita como uma série de novas funções, cada uma das quais usa apenas um parâmetro. O compilador executa essa operação automaticamente e é chamado de " currying ", em homenagem a Haskell Curry, um matemático que influenciou significativamente o desenvolvimento da programação funcional.
Para ver como o curry funciona na prática, vamos usar um exemplo de código simples que imprime dois números:
//   let printTwoParameters xy = printfn "x=%iy=%i" xy 
De fato, o compilador o reescreve aproximadamente da seguinte forma:
 //    let printTwoParameters x = //    let subFunction y = printfn "x=%iy=%i" xy //  ,    subFunction //   
Considere esse processo com mais detalhes:
- Uma função com o nome " printTwoParameters" éprintTwoParameters, mas aceita apenas um parâmetro: "x".
- Uma função local é criada dentro dela, que também aceita apenas um parâmetro: "y". Observe que a função local usa o parâmetro "x", mas x não é passado para ele como argumento. "x" está em um escopo que uma função aninhada pode vê-lo e usá-lo sem a necessidade de passá-lo.
- Finalmente, a função local recém-criada é retornada.
- A função retornada é então aplicada ao argumento "y". O parâmetro "x" é fechado para que a função retornada precise apenas do parâmetro "y" para concluir sua lógica.
Reescrevendo funções dessa maneira, o compilador garante que cada função aceite apenas um parâmetro, conforme necessário. Portanto, usando " printTwoParameters ", você pode pensar que esta é uma função com dois parâmetros, mas, de fato, uma função com apenas um parâmetro é usada. Você pode verificar isso passando apenas um argumento em vez de dois:
 //     printTwoParameters 1 //    val it : (int -> unit) = <fun:printTwoParameters@286-3> 
Se o calcularmos com um argumento, não obteremos um erro - a função será retornada.
Então, aqui está o que realmente acontece quando printTwoParameters é chamado com dois argumentos:
- printTwoParametersé- printTwoParameterscom o primeiro argumento (x)
- printTwoParametersretorna uma nova função na qual "x" está fechado.
- Em seguida, uma nova função é chamada com o segundo argumento (y)
Aqui está um exemplo de versões normais e passo a passo:
 //   let x = 6 let y = 99 let intermediateFn = printTwoParameters x //  -  // x   let result = intermediateFn y //     let result = (printTwoParameters x) y //   let result = printTwoParameters xy 
Aqui está outro exemplo:
 //  let addTwoParameters xy = x + y //   let addTwoParameters x = //   ! let subFunction y = x + y //      subFunction //   //       let x = 6 let y = 99 let intermediateFn = addTwoParameters x //  -  // x   let result = intermediateFn y //   let result = addTwoParameters xy 
Novamente, uma "função com dois parâmetros" é na verdade uma função com um parâmetro, que retorna uma função intermediária.
Mas espere, e o operador + ? Esta é uma operação binária que deve receber dois parâmetros? Não, também é curry, como outras funções. Essa é uma função chamada " + " que pega um parâmetro e retorna uma nova função intermediária, como addTwoParameters acima.
Quando escrevemos a expressão x+y , o compilador reordena o código de forma a converter o infixo em (+) xy , que é uma função chamada + que usa dois parâmetros. Observe que a função “+” precisa de parênteses para indicar que é usada como uma função regular, e não como um operador de infix.
Finalmente, uma função com dois parâmetros, chamada + , é tratada como qualquer outra função com dois parâmetros.
 //         let x = 6 let y = 99 let intermediateFn = (+) x //   ""  ""   let result = intermediateFn y //        let result = (+) xy //       let result = x + y 
E sim, isso funciona para todos os outros operadores e funções printf como printf .
 //    let result = 3 * 5 //    - let intermediateFn = (*) 3 //  ""  3   let result = intermediateFn 5 //    printfn let result = printfn "x=%iy=%i" 3 5 // printfn   - let intermediateFn = printfn "x=%iy=%i" 3 // "3"   let result = intermediateFn 5 
Assinaturas de função ao curry
Agora que sabemos como funcionam as funções ao curry, é interessante saber como serão as assinaturas.
Voltando ao primeiro exemplo, " printTwoParameter ", vimos que a função pegou um argumento e retornou uma função intermediária. A função intermediária também pegou um argumento e não retornou nada (ou seja, unit ). Portanto, a função intermediária era do tipo int->unit . Em outras palavras, o domínio printTwoParameters é int e range é int->unit . Juntando tudo, veremos a assinatura final:
 val printTwoParameters : int -> (int -> unit) 
Se você calcular a implementação explicada, poderá ver os colchetes na assinatura, mas se calcular a implementação ordinária implicada comum, não haverá colchetes:
 val printTwoParameters : int -> int -> unit 
Os suportes são opcionais. Mas eles podem ser representados na mente para simplificar a percepção das assinaturas de funções.
E qual é a diferença entre uma função que retorna uma função intermediária e uma função regular com dois parâmetros?
Aqui está uma função com um parâmetro que retorna outra função:
 let add1Param x = (+) x // signature is = int -> (int -> int) 
E aqui está uma função com dois parâmetros que retorna um valor simples:
 let add2Params xy = (+) xy // signature is = int -> int -> int 
Suas assinaturas são ligeiramente diferentes, mas, no sentido prático, não há muita diferença entre elas, exceto pelo fato de que a segunda função é automaticamente ativa.
Funções com mais de dois parâmetros
Como o curry funciona para funções com mais de dois parâmetros? Da mesma maneira: para cada parâmetro, exceto o último, a função retorna uma função intermediária que fecha o parâmetro anterior.
Considere este exemplo difícil. Declarei explicitamente os tipos de parâmetros, mas a função não faz nada.
 let multiParamFn (p1:int)(p2:bool)(p3:string)(p4:float)= () //   let intermediateFn1 = multiParamFn 42 // multoParamFn  int   (bool -> string -> float -> unit) // intermediateFn1  bool //   (string -> float -> unit) let intermediateFn2 = intermediateFn1 false // intermediateFn2  string //   (float -> unit) let intermediateFn3 = intermediateFn2 "hello" // intermediateFn3 float //     (unit) let finalResult = intermediateFn3 3.141 
Assinatura de toda a função:
 val multiParamFn : int -> bool -> string -> float -> unit 
e assinaturas de funções intermediárias:
 val intermediateFn1 : (bool -> string -> float -> unit) val intermediateFn2 : (string -> float -> unit) val intermediateFn3 : (float -> unit) val finalResult : unit = () 
A assinatura da função pode informar quantos parâmetros a função leva: basta contar o número de setas fora dos colchetes. Se a função aceitar ou retornar outra função, haverá mais setas, mas elas estarão entre colchetes e poderão ser ignoradas. Aqui estão alguns exemplos:
 int->int->int // 2  int  int string->bool->int //   string,  - bool, //  int int->string->bool->unit //   (int,string,bool) //    (unit) (int->string)->int //   ,  // ( int  string) //   int (int->string)->(int->bool) //   (int  string) //   (int  bool) 
Dificuldades com vários parâmetros
Até você entender a lógica por trás do curry, ele produzirá alguns resultados inesperados. Lembre-se de que você não receberá um erro se executar a função com menos argumentos do que o esperado. Em vez disso, você obtém uma função parcialmente aplicada. Se você usar a função parcialmente aplicada no contexto em que o valor é esperado, poderá obter um erro obscuro do compilador.
Considere uma função que é inofensiva à primeira vista:
 //   let printHello() = printfn "hello" 
O que você acha que acontecerá se você o chamar, como mostrado abaixo? O "olá" será impresso no console? Tente adivinhar antes da execução. Dica: veja a assinatura da função.
 //   printHello 
Ao contrário das expectativas, não haverá chamada. A função original espera unit como um argumento que não foi passado. Portanto, uma função parcialmente aplicada foi obtida (neste caso, sem argumentos).
E esse caso? Será compilado?
 let addXY xy = printfn "x=%iy=%i" x x + y 
Se você executá-lo, o compilador irá reclamar da linha com printfn .
 printfn "x=%iy=%i" x //^^^^^^^^^^^^^^^^^^^^^ //warning FS0193: This expression is a function value, ie is missing //arguments. Its type is ^a -> unit. 
Se não houver entendimento do curry, essa mensagem pode ser muito enigmática. O fato é que todas as expressões avaliadas separadamente (ou seja, não são usadas como um valor de retorno ou estão vinculadas a algo por meio de "let") devem ser avaliadas no valor unit . Nesse caso, não é calculado no valor unit , mas retorna uma função. É um longo caminho para dizer que printfn está faltando um argumento.
Na maioria dos casos, erros como esse acontecem ao interagir com uma biblioteca do mundo .NET. Por exemplo, o método Readline da classe TextReader deve Readline um parâmetro de unit . Muitas vezes você pode esquecer isso e não colocar colchetes; nesse caso, não é possível obter um erro do compilador no momento da "chamada", mas ele aparecerá quando você tenta interpretar o resultado como uma sequência.
 let reader = new System.IO.StringReader("hello"); let line1 = reader.ReadLine // ,    printfn "The line is %s" line1 //    // ==> error FS0001: This expression was expected to have // type string but here has type unit -> string let line2 = reader.ReadLine() // printfn "The line is %s" line2 //   
No código acima, a line1 é apenas um ponteiro ou delegado para o método Readline , não uma string, como você poderia esperar. Usar () no reader.ReadLine() realmente chamará a função.
Muitas opções
Você pode receber mensagens igualmente enigmáticas se passar muitos parâmetros para uma função. Alguns exemplos de passagem de muitos parâmetros para printf :
 printfn "hello" 42 // ==> error FS0001: This expression was expected to have // type 'a -> 'b but here has type unit printfn "hello %i" 42 43 // ==> Error FS0001: Type mismatch. Expecting a 'a -> 'b -> 'c // but given a 'a -> unit printfn "hello %i %i" 42 43 44 // ==> Error FS0001: Type mismatch. Expecting a 'a->'b->'c->'d // but given a 'a -> 'b -> unit 
Por exemplo, no último caso, o compilador relata que uma sequência de formato com três parâmetros é esperada (a assinatura 'a -> 'b -> 'c -> 'd possui três parâmetros), mas, em vez disso, uma sequência com dois é recebida (para a assinatura 'a -> 'b -> unit dois parâmetros).
Nos casos em que printf não é usado, passar um grande número de parâmetros geralmente significa que, em um certo estágio do cálculo, foi obtido um valor simples, para o qual o parâmetro está sendo tentado. O compilador ressentirá que um valor simples não é uma função.
 let add1 x = x + 1 let x = add1 2 3 // ==> error FS0003: This value is not a function // and cannot be applied 
Se dividirmos a chamada geral em uma série de funções intermediárias explícitas, como fizemos anteriormente, podemos ver o que exatamente está errado.
 let add1 x = x + 1 let intermediateFn = add1 2 //   let x = intermediateFn 3 //intermediateFn  ! // ==> error FS0003: This value is not a function // and cannot be applied 
Recursos Adicionais
Existem muitos tutoriais para F #, incluindo materiais para quem vem com experiência em C # ou Java. Os links a seguir podem ser úteis à medida que você avança no F #:
Várias outras maneiras de começar a aprender F # também são descritas.
Finalmente, a comunidade F # é muito amigável para iniciantes. Há um bate-papo muito ativo no Slack, suportado pela F # Software Foundation, com salas para iniciantes nas quais você pode participar livremente . É altamente recomendável que você faça isso!
Não se esqueça de visitar o site da comunidade de língua russa F # ! Se você tiver alguma dúvida sobre o aprendizado de um idioma, teremos prazer em discuti-los nas salas de bate-papo:
Sobre autores de tradução
Traduzido por @kleidemos
 As mudanças de tradução e editoriais foram feitas pelos esforços da comunidade de desenvolvedores de F # de língua russa . Agradecemos também a @schvepsss e @shwars pela preparação deste artigo para publicação.
 As mudanças de tradução e editoriais foram feitas pelos esforços da comunidade de desenvolvedores de F # de língua russa . Agradecemos também a @schvepsss e @shwars pela preparação deste artigo para publicação.