Qual é a diferença entre o tipo opcional e anulável

13

Swift tem Optionals. C # tem Nullabletipos.

Tanto quanto posso dizer, ambos servem ao mesmo propósito, além de algum tipo de valor, eles armazenam informações se a variável tem valor ou é indefinida (não inicializada).

A pergunta é: são Optionalsapenas Nullabletipos com nome diferente ou existem outras diferenças conceituais?

Em outras palavras, falar sobre o próprio conceito, ou no contexto de linguagens que não possuem Optionalsou Nullables, importa qual termo é usado?

Ao implementar essa funcionalidade na linguagem, importa se eu cito o tipo Optionals<T>ouNullable<T>

Dalija Prasnikar
fonte
Do ponto de vista agnóstico da linguagem, eles são idênticos. Dito isto, uma linguagem pode diferenciar os conceitos (por exemplo, se a correspondência de padrões é ou não aplicada).
Thomas Eding
O C # também pode ter opções (é apenas um padrão monádico): github.com/louthy/language-ext
Den
2
Usar uma referência nula (ou um ponteiro NULL, em idiomas com ponteiros) é um hack que alguns idiomas usam para representar valores opcionais, ou seja, um contêiner que pode ter um valor ou nenhum valor. Isso tem a limitação de que você pode considerar opcional apenas um tipo baseado em referências (ou ponteiros). Portanto, por exemplo, em Java, você não pode tornar um número inteiro opcional sem envolvê-lo em um objeto (por exemplo, Número Inteiro). Outros idiomas fornecem tipos opcionais como tipos genéricos que podem agrupar qualquer outro tipo, por exemplo, talvez em Haskell, Option em Scala e assim por diante.
Giorgio

Respostas:

5

Existe uma conotação diferente, mesmo que eles funcionem de maneira muito semelhante. Todo mundo, exceto a Microsoft (insira o olho aqui) usa nulle nullablesomente no contexto de referências. Optionse Maybesgeralmente são entendidos como referências e valores, especialmente em linguagens de programação funcionais em que transparência referencial significa que não há muita diferença entre um valor e uma referência.

Em outras palavras, falando sobre o próprio conceito, ou no contexto de linguagens que não possuem Optionalsou Nullables, importa qual termo é usado?

Optionsé o termo que causa a menor confusão entre um público amplo. Somente os programadores de C # pensam em a Nullablecomo potencialmente aplicável a um tipo de valor, e acho que a maioria deles tem pelo menos consciência do que Optioné.

Karl Bielefeldt
fonte
Todos, menos a Microsoft e os designers do SQL, acho que você quer dizer.
Jules
9

No .NET, existem duas categorias de tipo: referências e valores (int, double, structs, enumerações etc.). Entre suas diferenças está o fato de que uma referência pode ser null, enquanto um valor não pode. Portanto, se você tem um tipo de valor e deseja transmitir semântica "opcional" ou "desconhecida", é possível adorná-lo Nullable<>. Observe que Nullable<>é restrito por tipo para aceitar apenas tipos de valor (possui uma where T : structcláusula). Nullable<>também possui recursos especiais do compilador, pelo qual um nullvalor é protegido contra NullReferenceExceptions:

string x = null;
x.ToString(); // throws a NullReferenceException

int? y = null;
y.ToString(); // returns ""

Em linguagens funcionais (tais como Scala, F #, Haskell, Swift etc), é comum que nulla não existir . Isso ocorre porque, em geral, as pessoas consideram a existência de nulluma má idéia e os designers de linguagem decidiram resolver esse problema, impedindo-o.

Isso significa que, novamente, precisamos de alguma maneira de representar um não valor nesses idiomas. Digite o Optiontipo (a nomenclatura varia, é chamada Maybeem Haskell). Isso faz um trabalho semelhante ao Nullableque envolve um tipo para adicionar o caso em que o valor é "Nenhum" ou "Desconhecido" etc.

A diferença real está nas funções extras dadas a você pelas linguagens implementadas Option. Como exemplo, considere Option.map(em pseudocódigo):

function Option<T2> Option.map(opt: Option<T1>, mapFunc: T1 -> T2) {
    if (opt is None) return None
    else return Option<T2>(mapFunc(opt.Value))
}

Funções de encadeamento como Option.mapé uma maneira poderosa de evitar o típico clichê de verificação nula que você vê em qualquer lugar do C #:

if (service == null)
    return null;
var x = service.GetValue();
if (x == null || x.Property == null)
    return null;
return x.Property.Value;

O equivalente anulável em C # seria:

public static Nullable<T2> Map<T1, T2>(this Nullable<T1> nullable, Func<T1, T2> f)
    where T1 : struct
    where T2 : struct
{
    if (!nullable.HasValue) return (T2?)null;
    else return (T2?) f(nullable.Value);
}

No entanto, isso tem utilidade limitada em C # porque funcionará apenas para tipos de valor.

A nova versão do C # oferece o operador "propagação nula" ( ?.) que é semelhante à Option.mapfunção, exceto que é aplicável apenas a métodos e acessadores de propriedades. A amostra acima seria reescrita

return service?.GetValue()?.Property?.Value;
AlexFoxGill
fonte
C # é uma linguagem funcional impura (assim como F #). Também o F # tem nulos.
Den
O C # 6 tem coerção de valor nulo, portanto, pelo menos parte do seu código não está atualizado: github.com/dotnet/roslyn/wiki/… roslyn.codeplex.com/discussions/540883
Den
As opções também são fáceis de implementar: github.com/louthy/language-ext
Den
1
@ Den: C # se inclina para OOP enquanto F # se inclina para FP. A falta de currying e mutabilidade por padrão significa que o C # não é apropriado para programação funcional séria. Mencionei a propagação nula no final da resposta. As opções são realmente simples de implementar em C #, mas isso se afasta da questão.
AlexFoxGill
4
@ Den Eu acho que há uma má escolha de palavras em seus comentários. Uma linguagem funcional impura é uma linguagem funcional que permite efeitos colaterais. O C # pode ter alguns recursos funcionais e você pode usá-los com grande efeito, mas não é uma linguagem funcional. Eu acho que você quis dizer que nenhum idioma é 100% funcional, mas o F # está muito próximo do OCaml, que é, na maioria das contas, um idioma funcional. Quaisquer desvios que ele faz do paradigma funcional existem principalmente para que ele possa interoperar com código .NET externo. Caso em questão, nullnão é um valor válido para tipos de F #.
Doval