Pregunta

No soy capaz de entender el punto de Option[T] clase en Scala.Quiero decir, no puedo ver ninguna ventaja de None encima null.

Por ejemplo, considere el código:

object Main{
  class Person(name: String, var age: int){
    def display = println(name+" "+age)
  }

  def getPerson1: Person = {
    // returns a Person instance or null
  }

  def getPerson2: Option[Person] = {
    // returns either Some[Person] or None
  }

  def main(argv: Array[String]): Unit = {
    val p = getPerson1
    if (p!=null) p.display

    getPerson2 match{
      case Some(person) => person.display
      case None => /* Do nothing */
    }
  }
}

Ahora supongamos que el método getPerson1 devoluciones null, entonces la llamada realizada a display en primera línea de main está destinado a fallar con NPE.De manera similar si getPerson2 devoluciones None, el display La llamada volverá a fallar con algún error similar.

Si es así, ¿por qué Scala complica las cosas al introducir un nuevo contenedor de valores (Option[T]) en lugar de seguir un enfoque simple utilizado en Java?

ACTUALIZAR:

He editado mi código según @MitchLa sugerencia de.Todavía no puedo ver ninguna ventaja particular de Option[T].Tengo que probar lo excepcional. null o None en ambos casos.:(

Si he entendido bien desde La respuesta de @Michael, es la única ventaja de Option[T] es que le dice explícitamente al programador que este método podría devolver Ninguno?¿Es esta la única razón detrás de esta elección de diseño?

¿Fue útil?

Solución

Usted obtendrá el punto de Option mejor si usted se fuerza que nunca, nunca, el uso get. Esto se debe a get es el equivalente de "ok, me envía de vuelta a la tierra en cero".

Por lo tanto, tomar ese ejemplo de la suya. ¿Cómo llamaría usted a display sin utilizar get? Estas son algunas alternativas:

getPerson2 foreach (_.display)
for (person <- getPerson2) person.display
getPerson2 match {
  case Some(person) => person.display
  case _ =>
}
getPerson2.getOrElse(Person("Unknown", 0)).display

Ninguna de estas alternativas le permitirá llamar display en algo que no existe.

En cuanto a por qué existe get, Scala que no indicará cómo el código debe ser escrito. Se le puede pinchar suavemente, pero si quieres volver a caer a ninguna red de seguridad, es su elección.


clavado aquí:

  

es la única ventaja de la opción [T] se   que dice explícitamente el   programador que este método podría   Ninguno volver?

A excepción de la "única". Sin embargo, deseo reiterar que de otra manera: el principal ventaja de Option[T] sobre T es la seguridad de tipos. Se asegura que no va a enviar un método T a un objeto que puede no existir, ya que el compilador no le fallará.

Se dijo que hay que probar la capacidad de nulos en ambos casos, pero si usted se olvide - o no sabe - usted tiene que comprobar NULL, le dirá al compilador? ¿O sus usuarios?

Por supuesto, debido a su interoperabilidad con Java, Scala permite nulos tal como lo hace en Java. Así que si se utilizan bibliotecas de Java, si se utilizan bibliotecas de Scala mal escritos, o si utiliza mal escrito / bibliotecas personales Scala, usted todavía tiene que lidiar con los punteros nulos.

Otras dos ventajas importantes de Option que se me ocurren son:

  • Documentación: un tipo de método de firma le dirá si un objeto siempre se devuelve o no

  • .
  • componibilidad monádico.

Este último lleva mucho más tiempo para apreciar plenamente, y no es muy adecuado para ejemplos sencillos, ya que sólo se muestra su fuerza en código complejo. Por lo tanto, voy a dar un ejemplo a continuación, pero soy muy consciente de que casi no significará nada a excepción de las personas que reciben ya.

for {
  person <- getUsers
  email <- person.getEmail // Assuming getEmail returns Option[String]
} yield (person, email)

Otros consejos

Comparación:

val p = getPerson1 // a potentially null Person
val favouriteColour = if (p == null) p.favouriteColour else null

por:

val p = getPerson2 // an Option[Person]
val favouriteColour = p.map(_.favouriteColour)

La propiedad monádica bind , que aparece en Scala como el mapa de función , nos permite operaciones de la cadena en los objetos sin tener que preocuparse acerca de si son 'nulo' o no.

Tome este sencillo ejemplo, un poco más lejos. Decimos que queríamos encontrar todos los colores favoritos de una lista de personas.

// list of (potentially null) Persons
for (person <- listOfPeople) yield if (person == null) null else person.favouriteColour

// list of Options[Person]
listOfPeople.map(_.map(_.favouriteColour))
listOfPeople.flatMap(_.map(_.favouriteColour)) // discards all None's

O tal vez nos gustaría encontrar el nombre de la hermana de la madre del padre de una persona:

// with potential nulls
val father = if (person == null) null else person.father
val mother = if (father == null) null else father.mother
val sister = if (mother == null) null else mother.sister

// with options
val fathersMothersSister = getPerson2.flatMap(_.father).flatMap(_.mother).flatMap(_.sister)

Espero que esto arroja alguna luz sobre cómo las opciones pueden hacer la vida un poco más fácil.

La diferencia es sutil. Tenga en cuenta para ser realmente una función que debe devolver un valor - nulo no es realmente considerado como un "valor normal de retorno" en ese sentido, más de un tipo de fondo / nada.

Pero, en un sentido práctico, cuando se llama a una función que devuelve opcionalmente algo, lo haría:

getPerson2 match {
   case Some(person) => //handle a person
   case None => //handle nothing 
}

Por supuesto, se puede hacer algo similar con nula - pero esto hace que la semántica de llamar getPerson2 evidente por el hecho de que vuelve Option[Person] (una buena cosa práctica, aparte de depender de alguien que lee el documento y obtener una NPE porque no lea el documento).

Voy a tratar de desenterrar un programador funcional que puede dar una respuesta más estricta que pueda.

Para mí opciones son realmente interesantes cuando se maneja con la sintaxis de la comprensión. Tomando synesso ejemplo anterior:

// with potential nulls
val father = if (person == null) null else person.father
val mother = if (father == null) null else father.mother
val sister = if (mother == null) null else mother.sister

// with options
val fathersMothersSister = for {
                                  father <- person.father
                                  mother <- father.mother
                                  sister <- mother.sister
                               } yield sister

Si alguna de la asignación se None, será fathersMothersSister la None pero se levantó sin NullPointerException. A continuación, puede pasar con seguridad fathersMothersSisterto una función con parámetros de opciones sin tener que preocuparse. por lo que no comprueba por nula y no se preocupan de excepciones. Compare esto con la versión de Java presenta en synesso ejemplo.

Usted tiene bastante potentes capacidades de composición con Opción:

def getURL : Option[URL]
def getDefaultURL : Option[URL]


val (host,port) = (getURL orElse getDefaultURL).map( url => (url.getHost,url.getPort) ).getOrElse( throw new IllegalStateException("No URL defined") )

Tal vez alguien señaló que fuera, pero no lo vieron:

Una de las ventajas de coincidencia de patrones con la opción [L] vs. comprobación nula es que la opción es una clase cerrada, por lo que el compilador de Scala emitirá un aviso si usted se olvida de codificar o bien el Algunos o la Nada caso. Hay una bandera compilador para el compilador que dará vuelta a las advertencias en errores. Por lo que es posible prevenir el fracaso para manejar el caso "no existe" en tiempo de compilación en lugar de en tiempo de ejecución. Esto es una enorme ventaja sobre el uso del valor nulo.

No está ahí para ayudar a evitar un cheque nulo, que está ahí para forzar un cheque nulo. El punto se hace evidente cuando su clase tiene 10 campos, dos de los cuales podrían ser nulo. Y el sistema dispone de otras 50 clases similares. En el mundo Java, intenta evitar que los NPE en aquellos campos que utilizan una combinación de horesepower mental, la convención de nomenclatura, o tal vez incluso anotaciones. Y cada dev Java falla en este en un grado significativo. La clase Opción no sólo hace que los valores "anulables" visualmente claro para cualquier desarrollador que tratan de entender el código, pero permite que el compilador para hacer cumplir este contrato no leído.

[copiado de este comentario por Daniel Spiewak ]

  

Si la única manera de utilizar Option eran   modelar el partido con el fin de conseguir   valores fuera, entonces sí, estoy de acuerdo que   no mejora en absoluto sobre nula.   Sin embargo, se está perdiendo una enorme clase * *   de su funcionalidad. Lo único   razón de peso para utilizar Option es   si usted está utilizando su orden superior   funciones de utilidad. De manera eficaz,   necesita utilizar su naturaleza monádica.   Por ejemplo (suponiendo una cierta cantidad   de API recorte):

val row: Option[Row] = database fetchRowById 42
val key: Option[String] = row flatMap { _ get “port_key” }
val value: Option[MyType] = key flatMap (myMap get)
val result: MyType = value getOrElse defaultValue
     

No, no era tan ingenioso? Podemos   realmente hacer mucho mejor si usamos   for-comprensiones:

val value = for {
row <- database fetchRowById 42
key <- row get "port_key"
value <- myMap get key
} yield value
val result = value getOrElse defaultValue
     

Se dará cuenta de que somos * Nunca *   comprobar explícitamente nula, o Ninguno   cualquiera de su clase. El punto de   Opción es evitar nada de eso   comprobación. Usted acaba de cálculos de cuerda   a lo largo y pasar por la línea hasta que se   * * Realmente necesita para obtener un valor cabo. A   ese punto, puede decidir si desea o   No desea hacer la comprobación explícita   (Que debe no tienen que ver),   proporcionar un valor predeterminado, lanzar una   excepción, etc.

     

Yo nunca, nunca hacer ninguna coincidencia explícita   contra Option, y sé un montón de   otros desarrolladores Scala que están en el   mismo bote. David Pollack mencionó   yo el otro día que él usa   tal coincidencia explícita sobre Option (o   Box, en el caso de elevación) como un signo   que el desarrollador que escribió el código   no entender completamente el idioma   y su biblioteca estándar.

     

No me refiero a ser un martillo trol, pero   que realmente necesita para ver cómo   características del lenguaje son en realidad * * utilizan   en la práctica antes de que los bash como   inútil. Estoy totalmente de acuerdo en que   Opción es bastante uncompelling como * que *   usado, pero que no se esté usando el   forma en que fue diseñado.

Un punto que nadie más aquí parece haber planteado es que mientras que usted puede tener una referencia nula, hay una distinción introducida por opción.

Eso es que puede tener Option[Option[A]], que sería habitado por None, Some(None) y Some(Some(a)) donde a es uno de los habitantes habituales de A. Esto significa que si usted tiene algún tipo de contenedor, y desea ser capaz de almacenar punteros nulos en ella, y sacarlos, tiene que pasar de nuevo a un valor booleano adicional para saber si realmente tiene un valor fuera. Las verrugas como este abundan en el API de Java y los contenedores de algunas variantes sin bloqueo ni siquiera se puede proporcionarles.

null es una construcción de una sola vez, no componer con ella misma, que sólo está disponible para los tipos de referencia, y que le obliga a razonar de una manera no-total.

Por ejemplo, cuando se echa

if (x == null) ...
else x.foo()

tiene que llevar en la cabeza a lo largo de la rama else que x != null y que esto ya ha sido comprobado. Sin embargo, cuando se usa algo como opción

x match {
case None => ...
case Some(y) => y.foo
}

saber y no se Noneby construcción - y usted sabría que no estaba null bien, si no fuera por Hoare de billion dólar error .

Opción [T] es una mónada, que es muy útil cuando se utiliza funciones de orden superior para manipular los valores.

Te sugiero que lea los artículos enumerados a continuación, que son muy buenos artículos que muestran qué opción [L] es útil y cómo puede ser utilizado en forma funcional.

Adición al sumario de Randall de una respuesta , para entender por qué el potencial ausencia de un valor está representado por Option requiere la comprensión de lo Option comparte con muchos otros tipos de Scala-específicamente, los tipos de modelado mónadas. Si uno representa la ausencia de un valor con el nula, esa distinción ausencia-presencia no puede participar en los contratos compartidos por los otros tipos monádicos.

Si usted no sabe lo que son las mónadas, o si no se dan cuenta cómo están representados en la biblioteca de Scala, que no verá lo Option juega junto con, y no se puede ver lo que está perdiendo. Hay muchas ventajas a usar Option en lugar de nula de que sería digno de mención, incluso en ausencia de cualquier concepto de mónada (discuto algunos de ellos en el "Costo de Opción / Algunos vs nulo" scala-usuario de correo lista hilo aquí ), pero hablar de que el aislamiento es como hablar de un tipo de iterador concreto de aplicación lista enlazada, preguntándose por qué es necesario, a la vez que dejar de lado la interfaz más general contenedor / repetidor / algoritmo. Hay una interfaz más amplio en el trabajo aquí también, y Option proporciona un modelo de presencia-y-ausencia de esa interfaz.

Creo que la clave se encuentra en la respuesta de Synesso:La opción es no Principalmente útil como un alias engorroso para null, pero como un objeto completo que luego puede ayudarte con tu lógica.

El problema con null es que es el falta de un objeto.No tiene métodos que puedan ayudarle a manejarlo (aunque como diseñador de lenguaje puede agregar listas cada vez más largas de características a su lenguaje que emulen un objeto si realmente lo desea).

Una cosa que Option puede hacer, como ha demostrado, es emular nulo;luego debe probar el valor extraordinario "Ninguno" en lugar del valor extraordinario "nulo".Si lo olvidas, en cualquier caso, sucederán cosas malas.Esta opción hace que sea menos probable que esto suceda por accidente, ya que tienes que escribir "obtener" (lo que debería recordarte que puede ser null, er, quiero decir Ninguno), pero este es un pequeño beneficio a cambio de un objeto contenedor adicional.

Donde Option realmente comienza a mostrar su poder es ayudándote a lidiar con el concepto de "quería-algo-pero-en-realidad-no-tengo-uno".

Consideremos algunas cosas que quizás quieras hacer con cosas que podrían ser nulas.

Tal vez desee establecer un valor predeterminado si tiene un valor nulo.Comparemos Java y Scala:

String s = (input==null) ? "(undefined)" : input;
val s = input getOrElse "(undefined)"

¿En lugar de un algo engorroso?:constructo tenemos un método que trata con la idea de "usar un valor predeterminado si soy nulo".Esto limpia un poco tu código.

Quizás quieras crear un nuevo objeto sólo si tienes un valor real.Comparar:

File f = (filename==null) ? null : new File(filename);
val f = filename map (new File(_))

Scala es un poco más corto y nuevamente evita fuentes de error.Luego considere el beneficio acumulativo cuando necesita encadenar cosas como se muestra en los ejemplos de Synesso, Daniel y paradigmáticos.

no es un vasto mejora, pero si suma todo, vale la pena guardar en todas partes el código de muy alto rendimiento (donde desea evitar incluso la pequeña sobrecarga de crear el objeto contenedor Some(x)).

El uso de coincidencias no es realmente tan útil por sí solo, excepto como un dispositivo para alertarle sobre el caso nulo/Ninguno.Cuando es realmente útil es cuando empiezas a encadenarlo, por ejemplo, si tienes una lista de opciones:

val a = List(Some("Hi"),None,Some("Bye"));
a match {
  case List(Some(x),_*) => println("We started with " + x)
  case _ => println("Nothing to start with.")
}

Ahora puede combinar los casos Ninguno y los casos Lista vacía todos juntos en una declaración útil que extrae exactamente el valor que desea.

valores de retorno Null sólo están presentes para la compatibilidad con Java. No se debe utilizar de otra manera.

Es realmente una cuestión de estilo de programación. Uso de Java funcional, o escribiendo sus propios métodos de ayuda, que podría tener su funcionalidad opción, pero no abandonar el lenguaje Java:

http://functionaljava.org/examples/#Option.bind

El hecho de Scala incluye por defecto no lo hacen especial. La mayoría de los aspectos de los lenguajes funcionales están disponibles en la biblioteca y que pueden coexistir muy bien con otro tipo de código Java. Del mismo modo que se puede elegir para programar Scala con nulos se puede elegir para programar en Java sin ellos.

La admisión de antemano que se trata de una respuesta fácil, la opción es una mónada.

En realidad comparto la duda con usted. Acerca de la opción que realmente me molesta que 1) existe una sobrecarga en el rendimiento, ya que hay una lor de los "envoltorios" Algunos everywehre creados. 2) Tengo que usar un montón de unos y Opción en mi código.

Así que a ver ventajas y desventajas de esta decisión de diseño idioma hay que tener en cuenta alternativas. Como Java simplemente ignora el problema de la capacidad de nulos, no es una alternativa. La alternativa real proporciona lenguaje de programación Fantom. Hay tipos anulables y no anulables allí y?. ?: Operadores en lugar de un mapa de Scala / flatMap / getOrElse. Veo a los siguientes puntos en la comparación:

La ventaja de la opción:

  1. lenguaje más simple - no hay construcciones del lenguaje adicional requerida
  2. uniforme con otros tipos monádicos

La ventaja de anulable:

  1. sintaxis más corta en los casos típicos
  2. un mejor rendimiento (ya que no es necesario crear nuevos objetos opción y lambdas de mapa, flatMap)

Así que no hay ganador claro aquí. Y una nota más. No hay ninguna ventaja principal de sintáctica para el uso de la opción. Se puede definir algo como:

def nullableMap[T](value: T, f: T => T) = if (value == null) null else f(value)

O utilizar algunas conversiones implícitas para obtener la sintaxis de Pritty con puntos.

La verdadera ventaja de tener tipos de opciones explícitas es que son capaces de no usarlos en el 98% de todos los lugares, y por lo tanto de forma estática impedirá excepciones nulos. (Y en el otro 2% del tipo de sistema le recuerda que debe comprobar correctamente cuando en realidad se accede a ellos.)

Otra situación en la que trabaja opción, es en situaciones en que los tipos no son capaces de tener un valor nulo. No es posible almacenar nulo en un int, float, double, etc valor, pero con una opción que puede utilizar la Nada.

En Java, se tendría que usar las versiones en caja (entero, ...) de esos tipos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top