Padrões de função

Concluído(a)

Até agora, você pegou expressões repetitivas e criou funções com elas. Agora, o código está melhor e mais fácil de ser lido. Quanto mais você se acostuma a usar as funções, vale a pena começar a examinar alguns padrões poderosos que existem nesse espaço. Usando esses padrões, você obtém um código ainda mais fácil de ser lido e mantido.

Declarativo versus imperativo

Ao começar a codificação, provavelmente você escreve uma expressão. Depois, escreve outra expressão seguida por outra e assim em diante. Seu foco é resolver o problema e você está fazendo isso de um modo específico. Essa abordagem é conhecida como imperativa. Não há nada de errado com ela, pois ela resolve o problema em questão. Mas há outra rota a ser tomada, que é a abordagem declarativa. Um exemplo de abordagem declarativa é quando você consulta um banco de dados usando SQL.

Veja uma expressão de exemplo:

SELECT * 
FROM Students s
WHERE s.Location = "Ohio" 

O que faz com que esse código seja declarativo é que você pede o que deseja, mas não especifica como deseja que o problema seja resolvido. Você deixa o como para o SQL.

Essa abordagem também pode ser aplicada ao F#. O seguinte código usa uma abordagem declarativa:

let studentsFromOhio = 
    allStudents  
    |> filterLocation "Ohio"

No código anterior, você opera com base em dados e solicita o que deseja sem especificar como deve ser feito. Quando o código é semelhante ao mostrado acima, ele fica fácil de ser lido e analisado. Para chegar a esse ponto, vamos dar uma olhada em alguns padrões úteis compatíveis com o F#.

Padrões funcionais

Há alguns padrões úteis no F# que você pode usar para adotar uma abordagem mais funcional. Abordaremos os seguintes padrões:

  • Composição: uma composição combina várias funções em apenas uma.
  • Pipeline: um pipeline começa com um valor e depois chama várias funções em sequência usando a saída de uma função como entrada da próxima função.

Composição

A composição consiste na combinação e aplicação de funções, uma após a outra, em uma ordem específica. O operador de composição usa duas funções e retorna uma nova função.

Ao criar um código, muitas vezes você se encontra chamando uma função e depois outra função logo em seguida. Por exemplo, você quer solicitar uma lista e filtrar todos os produtos que estão com desconto.

No exemplo a seguir, a função add2() é chamada e seu resultado é fornecido como entrada para a função multiply3().

let add2 a = a + 2
let multiply3 a = a * 3 
let addAndMultiply a =
    let sum = add2 a
    let product = multiply3 sum
    product

printfn "%i" (addAndMultiply 2) // 12

Esse padrão é tão comum que o F# tem um operador para ele. O operador >> permite combinar duas ou mais funções em uma função maior. Usando o operador >>, você pode simplificar o código anterior, da seguinte forma:

let add2 a = a + 2
let multiply3 a = a * 3 
let addAndMultiply = add2 >> multiply3

printfn "%i" (addAndMultiply 2) // 12

A função combinada addAndMultiply() aplica as funções que a compõem da esquerda para a direita. Neste exemplo, add2() ocorre primeiro e multiply3() por último.

Pipeline

O operador de pipeline |> usa uma função e um argumento e retorna um valor. Vamos ver como o pipeline difere da composição com este exemplo:

let list = [4; 3; 1]
let sort (list: int list) = List.sort list
let print (list: int list)= List.iter(fun x-> printfn "item %i" x) list

list |> sort |> print // item 1 item 3 item 4

Na última linha de código, você começa com uma lista de inteiros, list, que serve como entrada para a primeira função, sort(). O resultado dessa operação é inserido em print(). A principal diferença entre pipeline e composição é que no pipeline, você começa com alguns dados, que são uma lista de inteiros nesse caso, e depois os passa por um conjunto de funções.