Ao criar um cliente para uma API da web em C #, deparei-me com um problema referente a null
um valor em que representaria duas coisas diferentes:
- nada , por exemplo, um
foo
pode ou não ter umbar
- desconhecido : por padrão, a resposta da API inclui apenas um subconjunto de propriedades, é necessário indicar quais propriedades adicionais você deseja. Tão desconhecido significa que a propriedade não foi solicitada da API.
Após algumas pesquisas, descobri o tipo Maybe (ou Option), como ele é usado em linguagens funcionais e como "resolve" problemas de cancelamento de referência nulos, forçando o usuário a pensar na possível ausência de um valor. No entanto, todos os recursos que encontrei falaram sobre a substituição de null por Maybe . Eu encontrei algumas menções à lógica de três valores , mas não a entendo completamente e, na maioria das vezes, sua menção estava no contexto de "é uma coisa ruim".
Agora estou me perguntando se faz sentido ter o conceito de null e Maybe , para representar desconhecido e nada, respectivamente. Essa é a lógica de três valores sobre a qual li ou tem outro nome? Ou é a maneira pretendida de aninhar um Talvez em um Talvez?
null
. É uma ideia completamente quebrada.M M x
eM x
deve ter a mesma semântica.Maybe a
Maybe Maybe a
UserInput a
M (M x)
eM x
deve ter a mesma semântica". Tomemos,M = List
por exemplo: listas de listas não são a mesma coisa que listas. QuandoM
é uma mônada, há uma transformação (nomeadamente a multiplicação mônada) a partirM (M x)
paraM x
o que explica a relação entre eles, mas eles não têm "a mesma semântica".Respostas:
O
null
valor como presente padrão em todos os lugares é apenas uma ideia realmente quebrada , então esqueça isso.Você sempre deve ter exatamente o conceito que melhor descreve seus dados reais. Se você precisar de um tipo que indique "desconhecido", "nada" e "valor", deve ter exatamente isso. Mas se não atender às suas necessidades reais, você não deve fazê-lo. É uma boa ideia ver o que outras pessoas usam e o que elas propuseram, mas você não precisa segui-las cegamente.
As pessoas que projetam bibliotecas padrão tentam adivinhar padrões de uso comuns, e geralmente o fazem bem, mas se houver algo específico de que você precisa, você deve defini-lo. Por exemplo, em Haskell, você pode definir:
Pode até ser o caso de você usar algo mais descritivo e mais fácil de lembrar:
Você pode definir 10 desses tipos para serem usados em diferentes cenários. A desvantagem é que você não terá funções de biblioteca preexistentes disponíveis (como as de
Maybe
), mas isso geralmente acaba sendo um detalhe. Não é tão difícil adicionar sua própria funcionalidade.fonte
Até onde eu sei, o valor
null
em C # é um valor possível para algumas variáveis, dependendo do seu tipo (estou certo?). Por exemplo, instâncias de alguma classe. Para o restante dos tipos (comoint
,bool
etc), você pode adicionar esse valor de exceção declarando variáveis comint?
ou embool?
vez disso (é exatamente isso que oMaybe
construtor faz, como descreverei a seguir).O
Maybe
construtor de tipos da programação funcional adiciona esse novo valor de exceção para um determinado tipo de dados. Portanto, seInt
é o tipo de números inteiros ouGame
o tipo de estado de um jogo,Maybe Int
possui todos os números inteiros mais um valor nulo (às vezes chamado de nada ). O mesmo paraMaybe Game
. Aqui, não há tipos que acompanham onull
valor. Você o adiciona quando precisar.Na OMI, essa última abordagem é a melhor para qualquer linguagem de programação.
fonte
Maybe
e largarnull
inteiramente?isinstance
testes de concreto . Scala também possui correspondência de padrões "adequada" também, e C♯ na verdade ganhou recentemente padrões simples também.final
(para uma classe que não pode ser herdada), ele também possuisealed
, para um classe que pode ser estendida apenas dentro da mesma unidade de compilação . Isso significa que o compilador pode conhecer estaticamente todas as subclasses possíveis (desde que sejam elas própriassealed
oufinal
) e, assim, fazer verificações exaustivas de correspondências de padrões, o que não é possível estaticamente para um idioma com carregamento de código de tempo de execução e herança irrestrita.int??
é ilegal em c #.Se você pode definir uniões de tipo, como
X or Y
, e se você tem um tipo nomeadoNull
que representa apenas onull
valor (e nada mais), então, para qualquer tipoT
,T or Null
na verdade não é tão diferenteMaybe T
. O único problemanull
é quando a linguagem trata um tipo deT
formaT or Null
implícita, criandonull
um valor válido para cada tipo (exceto os tipos primitivos nos idiomas que os possuem). É o que acontece, por exemplo, em Java, onde uma função que aceita aString
também aceitanull
.Se você tratar os tipos como conjunto de valores e
or
como união de conjuntos,(T or Null) or Null
representa o conjunto de valoresT ∪ {null} ∪ {null}
, que é o mesmo queT or Null
. Existem compiladores que realizam esse tipo de análise (SBCL). Como dito nos comentários, você não pode distinguir facilmente entreMaybe T
eMaybe Maybe T
quando visualiza os tipos como domínios (por outro lado, essa visualização é bastante útil para programas de tipo dinâmico). Mas você também pode manter os tipos como expressões algébricas, onde(T or Null) or Null
representa vários níveis do Maybes.fonte
Maybe (Maybe X)
não seja a mesma coisaMaybe X
.Nothing
não é a mesma coisa queJust Nothing
. ConflitarMaybe (Maybe X)
comMaybe X
torna impossível tratarMaybe _
polimorficamente.Você precisa descobrir o que deseja expressar e, em seguida, encontrar uma maneira de expressá-lo.
Primeiro, você tem valores no domínio do problema (como números, seqüências de caracteres, registros de clientes, booleanos etc.). Segundo, você tem alguns valores genéricos adicionais sobre os valores do domínio do problema: por exemplo "nada" (a ausência conhecida de um valor), "desconhecido" (nenhum conhecimento sobre presença ou ausência de um valor), não mencionado, foi " alguma coisa "(existe definitivamente um valor, mas não sabemos qual). Talvez você possa pensar em outras situações.
Se você quisesse representar todos os valores mais esses três valores adicionais, criaria uma enumeração com os casos "nada", "desconhecido", "alguma coisa" e "valor" e partir daí.
Em alguns idiomas, alguns casos são mais simples. Por exemplo, em Swift, você tem para todo tipo T o tipo "opcional T" que tem como valores possíveis nulos mais todos os valores de T. Isso permite lidar com muitas situações facilmente e é perfeito se você não tiver "nada" e " valor". Você pode usá-lo se tiver "desconhecido" e "valor". Você não pode usá-lo se precisar manipular "nada", "desconhecido" e "valor". Outros idiomas podem usar "null" ou "talvez", mas isso é apenas outra palavra.
No JSON, você tem dicionários com pares de chave / valor. Aqui, uma chave pode estar ausente de um dicionário ou pode ter um valor nulo. Portanto, descrevendo cuidadosamente como as coisas são armazenadas, você pode representar valores genéricos mais dois valores adicionais.
fonte