Tipos opacos são uma técnica de encapsulamento em Elm.
Algumas pessoas também chamam isso de ocultação de informações, ou ocultação de detalhes de implementação.
Eles também são comumente usados para garantir a validação, usando o padrão Parse, dont validate.
Por exemplo, você pode ter um tipo opaco EmailAddress, que só pode conter endereços de email válidos (ao contrário de uma String, que pode conter qualquer coisa).
Tipos personalizados têm uma ou mais variantes, e o compilador gera automaticamente uma função para criar cada variante, usando o nome da variante. Essas são a única forma de criar tipos personalizados. O compilador também permite desestruturação para essas variantes.
type Maybe a
= Nothing --> Variant implies a 'Nothing' function, with type 'Maybe a', and destructuring of 'Nothing'
| Just a --> Variant implies a 'Just' function, with type 'a -> Maybe a', and destructuring of 'Just _'
Por padrão, tudo isso é interno ao arquivo / módulo, e usamos a palavra-chave exposing para escolher o que exportar.
Para criar um tipo opaco, você simplesmente escolhe não exportar as variantes.
Quando você faz isso, quase sempre precisa fornecer outra forma de criar e obter as variantes, expondo funções personalizadas. Essas funções personalizadas permitem que você aplique alguma lógica ao criar e obter as variantes. Isso oculta os detalhes de implementação internos do módulo e significa que você pode alterar os detalhes de implementação o quanto quiser, desde que as funções expostas permaneçam as mesmas.
Provavelmente o uso mais comum de Tipos opacos é garantir a validação, usando o padrão Parse, dont validate.
Nesse padrão, você expõe uma função que recebe uma entrada com menos estrutura / segurança de tipos do que você deseja, como uma String, e então a parseia para um tipo mais estruturado, como EmailAddress, retornando o tipo ou retornando um erro (geralmente Maybe ou Result).
Por exemplo, no código abaixo, só é possível para módulos externos criar um tipo EmailAddress usando a função parseEmailAddress, então quando você está trabalhando com o tipo EmailAddress, você tem certeza de que ele é válido.
module EmailAddress exposing ( EmailAddress, parseEmailAddress, getEmailAddress )
-- The `EmailAddress` custom type has one variant, also called `EmailAddress`
-- When a custom type has one variant, it is idiomatic to use the same name for both.
type EmailAddress =
EmailAddress String
-- create an EmailAddress variant
parseEmailAddress: String -> Maybe EmailAddress
parseEmailAddress candidateEmailAddress =
if isValidEmailAddress candidateEmailAddress then
Just (EmailAddress candidateEmailAddress)
else
Nothing
-- get an EmailAddress variant
getEmailAddress: EmailAddress -> String
getEmailAddress ( EmailAddress emailAdress ) =
emailAdress
isValidEmailAddress: String -> bool
-- ...
Neste exercício, você vai escrever um SecureTreasureChest genérico (e mágico!) para guardar um tesouro.
Quem prestou atenção deve ter notado que o TreasureChest anterior não era tão seguro assim!
Se você usasse a função getTreasure, precisava informar a senha, mas ainda podia simplesmente desestruturar o tipo TreasureChest para pegar o tesouro, sem precisar saber a senha.
Pior ainda: você conseguia até recuperar a senha do mesmo jeito!
Vamos resolver isso usando um Tipo Opaco.
Password, usando o padrão "Parse, don't validate"Defina um tipo customizado Password com uma única variante.
A variante deve ter um valor String associado, para a senha.
Você deve expor o tipo Password, mas não a variante.
Isso torna Password um Tipo Opaco e significa que você não pode mais usar desestruturação para pegar a senha.
Não é possível testar se um tipo é opaco ou não; em código de produção, você usaria o elm-review (uma ferramenta de análise estática) para garantir isso.
Defina e exponha uma função createPassword, com uma definição de tipo, que recebe uma String e retorna um Maybe Password.
A Owasp recomenda que as senhas tenham pelo menos 8 caracteres, então vamos usar o padrão Parse, don't validate para garantir essa invariante.
Se a senha tiver menos de 8 caracteres, createPassword deve retornar Nothing; caso contrário, deve retornar Just Password.
SecureTreasureChest, mas não a sua varianteVocê deve expor o tipo SecureTreasureChest, mas não a variante.
Isso torna SecureTreasureChest um Tipo Opaco e significa que você não pode mais usar desestruturação para pegar a senha ou o tesouro.
createTreasure
Essa função deve receber dois parâmetros e ter uma definição de tipo.
Password (para a senha)A função deve retornar um SecureTreasureChest.
Exponha essa função para que outros módulos possam criar instâncias de SecureTreasureChest.
getTreasure
Essa função deve receber dois parâmetros e ter uma definição de tipo.
String (para a senha)SecureTreasureChest
Essa função deve conferir a tentativa de senha fornecida com a Password do SecureTreasureChest.
A função deve retornar um Maybe.
Se as senhas coincidirem, retorne Just com o valor genérico do SecureTreasureChest (o tesouro!)
Se as senhas não coincidirem, retorne Nothing.
Exponha essa função para que outros módulos possam pegar o tesouro de um SecureTreasureChest, desde que informem a senha correta.
Crie sua conta no Exercism para aprender e dominar Elm com 28 conceitos110 exercícios e mentoria humana de verdade, tudo de graça.