Padrões de função
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.