Al crear un cliente para una API web en C #, me encontré con un problema relacionado null
con un valor en el que representaría dos cosas diferentes:
- nada , por ejemplo, un
foo
puede o no tener unbar
- desconocido : por defecto, la respuesta API solo incluye un subconjunto de propiedades, debe indicar qué propiedades adicionales desea. Tan desconocido significa que la propiedad no se solicitó a la API.
Después de algunas búsquedas, descubrí el tipo Quizás (u Opción), cómo se usa en lenguajes funcionales y cómo "resuelve" problemas de anulación de referencia al obligar al usuario a pensar en la posible ausencia de un valor. Sin embargo, todos los recursos que encontré hablaron de reemplazar nulo con Quizás . Encontré algunas menciones de lógica de tres valores , pero no lo entiendo completamente y la mayoría de las veces su mención fue en el contexto de "es algo malo".
Ahora me pregunto si tiene sentido tener tanto el concepto de nulo como el de Quizás , para representar lo desconocido y nada, respectivamente. ¿Es esta la lógica de tres valores sobre la que leí, o tiene otro nombre? ¿O es la forma prevista de anidar un tal vez en un tal vez?
null
. Es una idea completamente rota.M M x
yM x
debería tener la misma semántica.Maybe a
Maybe Maybe a
UserInput a
M (M x)
yM x
debería tener la misma semántica". TomemosM = List
por ejemplo: las listas de listas no son lo mismo que las listas. CuandoM
es una mónada, hay una transformación (es decir, la multiplicación de la mónada) a partirM (M x)
de laM x
cual se explica la relación entre ellos, pero no tienen "la misma semántica".Respuestas:
El
null
valor como presente predeterminado en todas partes es solo una idea realmente rota , así que olvídate de eso.Siempre debe tener exactamente el concepto que mejor describa sus datos reales. Si necesita un tipo que indique "desconocido", "nada" y "valor", debe tener precisamente eso. Pero si no se ajusta a sus necesidades reales, entonces no debe hacerlo. Es una buena idea ver qué usan otras personas y qué han propuesto, pero no tiene que seguirlas a ciegas.
Las personas que diseñan bibliotecas estándar intentan adivinar patrones de uso comunes, y generalmente lo hacen bien, pero si hay algo específico que necesita, debe definirlo usted mismo. Por ejemplo, en Haskell, podría definir:
Incluso puede darse el caso de que deba usar algo más descriptivo que sea más fácil de recordar:
Podría definir 10 de estos tipos para ser utilizados en diferentes escenarios. El inconveniente es que no tendrá funciones de biblioteca preexistentes disponibles (como las de
Maybe
), pero esto generalmente resulta ser un detalle. No es tan difícil agregar su propia funcionalidad.fuente
Hasta donde sé, el valor
null
en C # es un valor posible para algunas variables, dependiendo de su tipo (¿estoy en lo cierto?). Por ejemplo, instancias de alguna clase. Para el resto de los tipos (comoint
,bool
etc.) puede agregar este valor de excepción declarando variables conint?
o en subool?
lugar (esto es exactamente lo que hace elMaybe
constructor, como describiré a continuación).El
Maybe
constructor de tipos de la programación funcional agrega este nuevo valor de excepción para un tipo de datos dado. Entonces, siInt
es el tipo de enteros oGame
es el tipo de estado de un juego, entoncesMaybe Int
tiene todos los enteros más un valor nulo (a veces llamado nada ). Lo mismo paraMaybe Game
. Aquí, no hay tipos que vienen con elnull
valor. Lo agrega cuando lo necesita.En mi opinión, este último enfoque es el mejor para cualquier lenguaje de programación.
fuente
Maybe
y soltar pornull
completo?isinstance
pruebas concretas . Scala también tiene una coincidencia de patrones "adecuada", y C♯ en realidad también obtuvo patrones simples recientemente.final
(para una clase que no se puede heredar de), también tienesealed
, para un clase que solo se puede extender dentro de la misma unidad de compilación . Esto significa que el compilador puede conocer estáticamente todas las subclases posibles (siempre que todas sean ellas mismassealed
ofinal
) y, por lo tanto, realizar comprobaciones exhaustivas de coincidencias de patrones, lo que no es posible estáticamente para un idioma con carga de código de tiempo de ejecución y herencia sin restricciones.int??
es ilegal en C #.Si puede definir uniones de tipo, como
X or Y
, y si tiene un tipo llamadoNull
que representa solo elnull
valor (y nada más), entonces, para cualquier tipoT
, enT or Null
realidad no es tan diferente deMaybe T
. El único problema connull
es cuando las golosinas de idioma un tipoT
comoT or Null
implícita, por lo quenull
un valor válido para todo tipo (excepto los tipos primitivos en lenguajes que los tienen). Esto es lo que sucede, por ejemplo, en Java, donde una función que toma aString
también aceptanull
.Si trata los tipos como un conjunto de valores y
or
como una unión de conjuntos,(T or Null) or Null
representa el conjunto de valoresT ∪ {null} ∪ {null}
, que es lo mismo queT or Null
. Hay compiladores que realizan este tipo de análisis (SBCL). Como se dijo en los comentarios, no puede distinguir fácilmente entreMaybe T
yMaybe Maybe T
cuando ve los tipos como dominios (por otro lado, esa vista es bastante útil para los programas de tipo dinámico). Pero también podría mantener los tipos como expresiones algebraicas, donde(T or Null) or Null
representa múltiples niveles de Maybes.fuente
Maybe (Maybe X)
no sea lo mismo queMaybe X
.Nothing
No es lo mismo queJust Nothing
. La combinaciónMaybe (Maybe X)
conMaybe X
hace imposible el tratamientoMaybe _
polimórfico.Necesita averiguar lo que desea expresar y luego encontrar la forma de expresarlo.
Primero, tiene valores en su dominio problemático (como números, cadenas, registros de clientes, booleanos, etc.). En segundo lugar, tiene algunos valores genéricos adicionales además de los valores de su dominio problemático: por ejemplo, "nada" (la ausencia conocida de un valor), "desconocido" (sin conocimiento sobre la presencia o ausencia de un valor), no mencionado fue " algo "(definitivamente hay un valor, pero no sabemos cuál). Quizás puedas pensar en otras situaciones.
Si quisiera poder representar todos los valores más estos tres valores adicionales, crearía una enumeración con los casos "nada", "desconocido", "algo" y "valor" e iría desde allí.
En algunos idiomas, algunos casos son más simples. Por ejemplo, en Swift tiene para cada tipo T el tipo "T opcional" que tiene sus posibles valores nulo más todos los valores de T. Esto le permite manejar fácilmente muchas situaciones, y es perfecto si tiene "nada" y " valor". Puede usarlo si tiene "desconocido" y "valor". Usted podría no usarlo si usted necesita para manejar "nada", "desconocido" y "valor". Otros idiomas pueden usar "nulo" o "tal vez", pero esa es solo otra palabra.
En JSON, tiene diccionarios con pares clave / valor. Aquí, una clave puede faltar en un diccionario o puede tener un valor nulo. Entonces, al describir cuidadosamente cómo se almacenan las cosas, puede representar valores genéricos más dos valores adicionales.
fuente