Use lentes para atualizar registros aninhados (algo específico de linguagens com dados imutáveis).
Atualizar campos de registros aninhados e imutáveis é meio chato. O código para esses casos é tão trabalhoso quanto profunda é a estrutura. Se você tiver, digamos, um Person, que contém um Address, que tem uma Street, que tem um Number, atualizar o Number exige criar uma nova Street com o novo Number, depois um novo Address com a nova Street e, por fim, um novo Person com o novo Address. Já se perdeu?
Uma solução para esse problema é usar lentes.
Implemente várias funções de acesso a registros usando lentes. A suíte de testes também permite que você dispense as lentes por completo, assim você pode experimentar abordagens diferentes.
Lentes não são muito usadas em Roc porque a sintaxe de atualização de registros cobre bem a maioria dos casos. Por exemplo, considere este registro aninhado:
bob = { hair: { color: Brown, length: Long }, mouth: Smiling, height: 6.0 }
Agora vamos dar um corte de cabelo no Bob:
new_bob = { ..bob, hair: { ..bob.hair, length: Short } }
Não precisa de lentes! Dito isso, mesmo que lentes sejam raras em Roc, elas são comuns em outras linguagens, como Haskell e Scala, então talvez você queira saber como alguém as implementaria em Roc. Também é uma boa forma de praticar alguns recursos avançados de Roc, como tipos parametrizados e registros que contêm funções. De fato, aqui está uma forma de representar uma lente em Roc:
Lens(whole, part) := {
get : whole -> part,
set : whole, part -> whole,
}
Este é um tipo parametrizado: whole e part podem ser quaisquer tipos. whole normalmente é um registro, enquanto part pode ser qualquer tipo de campo. O campo get contém uma função que retorna um part específico do whole informado, enquanto o campo set contém uma função que retorna uma cópia do whole informado, só que com um part específico substituído pelo part informado.
Por exemplo, aqui está uma lente para obter ou definir o cabelo de uma pessoa:
hair_lens = {
get: |person| person.hair,
set: |person, hair| { ..person, hair },
}
Podemos usar esta lente para focar no cabelo do Bob, assim:
bob_hair = bob |> hair_lens.get
E podemos mudar o cabelo dele assim:
new_hair = { ..bob.hair, length: Short }
new_bob = bob |> (hair_lens.set)(new_hair)
Nota: os parênteses em volta de hair_lens.set são obrigatórios: sem eles, Roc procuraria uma função chamada set no tipo de hair_lens.
As lentes se tornam especialmente úteis quando você começa a compô-las, permitindo obter e definir partes profundamente aninhadas. Isso pode ser feito implementando uma função como esta:
compose : Lens(outer, inner), Lens(inner, part) -> Lens(outer, part)
Depois que essa função estiver implementada, você pode usá-la para criar uma lente de comprimento de cabelo assim:
length_lens = {
get: |hair| hair.length,
set: |hair, length| { ..hair, length },
}
hair_length_lens = compose(hair_lens, length_lens)
Agora você pode dar outro corte de cabelo no Bob:
set_hair_length = hair_length_lens.set
new_bob = bob |> set_hair_length(Short)
Você também pode querer implementar uma função que permita aplicar qualquer transformação a uma parte aninhada:
over : Lens(whole, part), whole, (part -> part) -> whole
Depois de implementada, você pode usar over assim:
cut_hair = |current_length| match current_length {
Long => Short
Short => Bald
Bald => Bald
}
new_bob = hair_length_lens |> over(bob, cut_hair)
Neste exercício, os testes vão verificar se o seu código consegue modificar várias partes aninhadas do seguinte registro aninhado:
test_person : Person
test_person = {
name: { forenames: "Jane Joanna", surname: "Doe" },
birth: {
born_at: { street: "Longway", house_number: 1024, place: "Springfield", country: "United States" },
born_on: { year: 1984, month: 4, day: 12 },
},
address: { street: "Shortlane", house_number: 2, place: "Fallmeadow", country: "Canada" },
}
Recomendamos que você primeiro passe nos testes usando a sintaxe de atualização de registros de Roc e depois tente usar lentes.
Crie sua conta no Exercism para aprender e dominar Roc com 125 exercícios e mentoria humana de verdade, tudo de graça.