F # 9: Opção de tipo

Se C # tiver o conceito de nulo para tipos de referência e Nullabe para estruturas. Isso pode assumir uma das 2 formas a seguir (para demonstração, eu uso o tipo int aqui, mas o tipo pode ser qualquer estrutura).

  • Anulável
  • int?

Isenção de responsabilidade
   Nullabe  Nullable<T> ,    - ,    


Ambos são equivalentes.

A classe Nullable fornece várias propriedades e métodos auxiliares que facilitam o trabalho com tipos e estruturas nulos. Estes são os seguintes:

  • Hasvalue
  • Valor
  • GetValueOrDefault ()
  • GetValueOrDefault (T)

Há algo um pouco diferente de F # na forma de um tipo de opção , que é um tipo mais amigável de F # que prefere não lidar com nulo, mas prefere lidar com itens em termos de "Pode conter valor de tipo" ou "Pode não ter valor ". Parece Nullable, mas no final é do tipo F #, então você pode esperar que seja usado normalmente em coisas do F #.

Outra coisa que vale a pena notar com o tipo de opção F # é que ele pode ser usado para qualquer tipo, não apenas para estruturas. Isso é diferente do .NET Nullable, que só pode ser usado com estruturas.

O valor Nenhum é usado quando não há valor; caso contrário, a expressão Some (...) atribui um valor. Os valores Some e None podem ser usados ​​ao combinar com um padrão, um exemplo dos quais veremos neste post.

Como Nullable, o tipo de opção F # possui várias propriedades / métodos auxiliares, mostrados na tabela abaixo.

Nenhuma
Opção 'T
Uma propriedade estática que permite criar um valor de parâmetro com o valor Nenhum.
Isnone
bool
Retorna true se o parâmetro for None.
Issome
bool
Retorna true se o parâmetro tiver um valor diferente de Nenhum.
Alguns
Opção 'T
Um membro estático que cria um parâmetro cujo valor não é Nenhum.
Valor
'T
Retorna um valor base ou lança uma NullReferenceException se o valor for Nenhum.

Criando Opções


Portanto, agora que sabemos o que são os tipos de opção, como os criamos. Vejamos alguns exemplos. Observe que, neste exemplo, usei os métodos auxiliares IsSome / IsNone. Pessoalmente, acredito que comparar com uma amostra é a melhor maneira, pois ajudará você a comparar todos os casos, incluindo a opção Não.

De fato, mostrarei como é fácil cometer erros quando você trabalha com tipos de opção, se você decidir usar métodos auxiliares, mas primeiro vamos ver um exemplo do caso correto.

 let someInt = Some(43) let someString = Some("cat") let printTheOption (opt :Option<'a>) = printfn "Actual Option=%A" opt printfn "IsNone=%b, IsSome=%b Value=%A\r\n\r\n" opt.IsNone opt.IsSome opt.Value printfn "let someInt = Some(43)" printfn "let someString = Some(\"cat\")" printfn "printTheOption (someInt)" printTheOption someInt printfn "printTheOption (someString)" printTheOption someString 

imagem

Mas e se tentarmos novamente usando esse código, onde temos Nenhum para o valor da Opção, que passaremos para a função printTheOption:

 let demoNone = None let printTheOption (opt :Option<'a>) = printfn "Actual Option=%A" opt printfn "IsNone=%b, IsSome=%b Value=%A\r\n\r\n" opt.IsNone opt.IsSome opt.Value printfn "let demoNone = None" printfn "printTheOption demoNone" printTheOption demoNone 

imagem

Como você pode ver, temos um problema aqui. O problema é que tentamos obter o valor Option usando a propriedade auxiliar Option.Value, nesse caso, é None, então obtemos uma NullReferenceException. É mostrado na tabela acima que, quando você usa propriedades e métodos auxiliares, pode obter uma exceção. Bem, você poderia usar o método IsNone e sempre verificaria o valor usando isso quando pudesse usar apenas uma boa correspondência de padrão limpo.

Se você não pode aceitar isso, pergunte-se quantas vezes você teve que verificar o valor nulo ao usar o C #. Isso até levou as pessoas a incluir construções funcionais, como Maybe Null Monad, no código .NET normal.

Portanto, agora que vimos o perigo de usar métodos / propriedades auxiliares, agora vamos voltar nossa atenção para como podemos evitar essas exceções:

 et printTheOption (opt :Option<'a>) = match opt with | Some a -> printfn "opt is Some, and has value %A" a | None -> printfn "opt is None" let demoNone = None let someInt = Some 1 let someString = Some "crazy dude in the monkey house" printTheOption demoNone printTheOption someInt printTheOption someString 

Minha opinião pessoal é que é mais legível do que o código que seria pontilhado com IsSome / IsNone em todos os lugares. É claro que isso é da conta de todos, mas o fato de termos abordado todos os princípios básicos nesta função simples não pode ser ignorado.

imagem

Option vs Null


Então, falamos sobre Option no F # em comparação com Nullabe, e sabemos que o tipo Option pode ser usado com qualquer tipo, enquanto Nullable só pode ser usado com estruturas. Mas e os tipos de opção em comparação com os tipos de referência regulares no .NET. Bem, uma grande vitória para o Option é que, quando você usa o tipo de referência no .NET, está lidando com uma referência de ponteiro, que, como tal, pode ser definida como nula. No entanto, o tipo do objeto permanece o mesmo que foi declarado, o que pode conter uma referência válida para o objeto no heap (Heap) ou pode ser nulo.

Seria normal escrever assim:

 string s1 = "cats"; int s1Length = s1.Length; string s2 = null; int s2Length = s2.Length; 

Isso será compilado com sucesso. No entanto, quando executamos isso, obteremos uma NullReferenceException, pela qual seremos forçados a sair para proteger todo o código da possível presença de null. Isso se torna tedioso rapidamente, mesmo se você tiver uma classe de proteção pequena e agradável que verificará o valor e o tratará / lançará uma exceção mais significativa.

Esta captura de tela usa o LinqPad, por isso pode parecer um pouco incomum se você nunca viu o LinqPad antes, mas confie em mim, você ainda obtém o mesmo resultado em um IDE diferente.

imagem

Agora vamos ver como será o código equivalente em F #

 let s1 = "Cats" let s1Length = s1.Length; let s2 = None let s2Length = s2.Length; //excplicily string typed None let s3 = Option.None let s3Length = s3.Length; 

Nesse código, você receberá um erro de compilação imediato, pois o s3 será considerado outro tipo que não possui a propriedade "Length".

Comparação de opções


Os tipos de opção são considerados iguais, são do mesmo tipo e os tipos que contêm são iguais, o que obedece às regras de igualdade do tipo retido.

Portanto, esse tipo de coisa pode levar a um erro imediato em tempo de compilação em F #

 let o1 = Some 1 let o2 = Some "Cats" printfn "o1=o2 : %b" (o1 = o2) 

imagem

Isso funcionará conforme o esperado, pois os tipos são os mesmos

 let o1 = Some "Cats" let o2 = Some "Cats" let o3 = Some "cats" printfn "o1=o2 : %b" (o1 = o2) printfn "o2=o3 : %b" (o2 = o3) 

imagem

Source: https://habr.com/ru/post/pt470918/


All Articles