2012-03-14 19 views
22

Aprendo Scala durante un tiempo y no puedo entender claramente el uso de la opción. Me ayuda a evitar las comprobaciones nulas cuando cadenas de funciones (según docs). Eso está claro para mí :)Cuándo utilizar la opción

A continuación veo que la opción puede ser una especie de indicador para el desarrollador de que el valor nulo es posible aquí y debe manejarse. ¿Es verdad? En caso afirmativo, ¿debo usar Option donde sea posible? Por ejemplo

class Racer { 
    val car = new Car() 
} 

tengo clase Racer y estoy seguro de que el campo coche no puede ser nulo (porque es constante y obtener un valor al crear instancia Racer). No hay necesidad de opción aquí.

class Racer { 
    var car = new Car() 
} 

Aquí lo hago para que el automóvil pueda cambiar. Y es posible que alguien asigne un nulo al automóvil. ¿Debería usar Option aquí? Si es así, noto que todos mis campos de clase son candidatos para Opción. Y mi código se ve así

class Racer { 
    var car: Option[Car] = None 
    var currentRace: Option[Race] = None 
    var team: Option[Team] = None 
    ... 
} 

¿Se ve bien? Para mí, parece una especie de Opción sobreutilizando.

def foo(): Result = { 
    if(something) 
     new Result() 
    else 
     null 
} 

Tengo un método que puede devolver nulo. ¿Debo devolver la opción en su lugar? ¿Debo hacerlo siempre si es posible que el método devuelva nulo?

Cualquier idea al respecto sería útil. ¡Gracias por adelantado!

Mi pregunta es similar a Why option pero creo que no es lo mismo. Se trata más de cuando, no por qué. :)

+1

Además de otras respuestas intente leer este http://simply.liftweb.net/index-7.2.html –

+0

Gracias. Le daré un vistazo. – Soteric

Respuesta

34

Debe evitar null tanto como sea posible en Scala. Realmente solo existe para la interoperabilidad con Java. Por lo tanto, en lugar de null, use Option siempre que sea posible que una función o método pueda devolver "sin valor", o cuando sea lógicamente válido para una variable miembro que tenga "ningún valor".

Con respecto a su ejemplo Racer: ¿Es realmente un Racer válido si no tiene un car, currentRace y team? Si no, entonces no deberías hacer esas opciones de variables miembro. No solo les haga opciones porque teóricamente es posible asignarlos al null; hágalo solo si lógicamente lo considera un objeto Racer válido si alguna de esas variables miembro no tiene valor.

En otras palabras, es mejor pretender que null no existe. El uso de null en el código Scala es un olor a código.

def foo(): Option[Result] = if (something) Some(new Result()) else None 

Tenga en cuenta que Option tiene muchos métodos útiles para trabajar.

val opt = foo() 

// You can use pattern matching 
opt match { 
    case Some(result) => println("The result is: " + result) 
    case None   => println("There was no result!") 
} 

// Or use for example foreach 
opt foreach { result => println("The result is: " + result) } 

Además, si se desea programar en el estilo funcional, se debe evitar que los datos mutables tanto como sea posible, lo que significa: evitar el uso de var, utilice val lugar, utilizar colecciones inmutables y hacer sus propias clases Inmutable tanto como sea posible.

+0

Gran respuesta. Tiene sentido. Gracias. – Soteric

+11

Además de "pretender [ing] como si' null' no existe ", si tiene que tratar con el código de Java que le da' foo' que podría ser 'nulo', puede usar' Option (foo) ' , que convertirá 'null' en' None', y cualquier otra cosa en 'Some (blah)'. –

6

La idea detrás de Options es deshacerse de null, entonces sí, debe usarlos en lugar de devolver "null o un valor". También es por eso que, si desea modelar campos opcionales (relación 0..1 con otros objetos), usar Option definitivamente es algo bueno.

El pequeño inconveniente es que hace que la declaración de clase con una gran cantidad de campo opcional sea un tanto prolijo, como usted mencionó.

Una cosa más, en Scala, se recomienda utilizar "objetos inmutables", por lo que en su ejemplo, los campos deben ser algunos val s. ;)

+0

En caso de que use val ¿cómo puedo cambiar el valor? ¿Qué pasa si quiero un auto nuevo para mi corredor? – Soteric

+4

La idea es crear un objeto nuevo cuando ocurre un evento como un auto nuevo: 'class Racer (nombre: String, auto: Option [Car]) {def receiveANewCar (c: Car) = new Racer (name, Some (c)); def brokeHisCar = new Racer (name, None)} ' – Nicolas

9

Cuando se trata de programación funcional en Scala, Option es mucho más preferible que null ya que es seguro para el tipo y funciona bien con otras construcciones en el paradigma funcional.

Especialmente, puede escribir fácilmente código idiomático utilizando funciones de orden alto en Option. Esta Scala Option Cheat Sheet es una lectura útil sobre el tema.

7

Mientras que Option puede hacer que su definición de clase parezca prolija, la alternativa es no saber cuándo se necesita probar si se define una variable. Creo que en su ejemplo, si su clase fuera inmutable (todos los campos eran val s), no necesitaría tantos Option s.

Para su método foo, si devuelve nulo, debe documentarlo y el cliente debe leer esa documentación. A continuación, el cliente va a escribir un montón de código como:

val result = foo(x) 
if (result != null) { 
    println(result) 
} 

Si en cambio se define foo devolver un Option[Result], el sistema de tipos obliga al cliente a manejar la posibilidad de un resultado no definido. También tienen todo el poder de las clases de colecciones.

result foreach println 

Una ventaja adicional de utilizar el método de colecciones con un Option en lugar de pruebas para null es que si el método se extendió a una colección de múltiples elementos (como un List), es posible que no tenga que cambiar su código en absoluto. Por ejemplo, inicialmente puede suponer que su Racer solo puede tener un solo equipo, por lo que debe definir val crew: Option[Crew] y puede probar si el equipo tiene más de 30 años con crew.forall(_.age > 30).getOrElse(false). Ahora si ha cambiado la definición de val crew: List[Crew] el código antiguo todavía compilar y ahora se comprobaría si todos los miembros de su tripulación son mayores de 30 años

A veces es necesario para hacer frente a null ya que las bibliotecas que está usando puede devolverlo. Por ejemplo, cuando obtiene un recurso, puede obtener un resultado de null. Afortunadamente, es fácil envolver a la defensiva los resultados para que se transformen en una opción.

val resource = Option(this.getClass.getResource("foo")) 

Si getResource regresaron a continuación nullresource es igual a None, y de lo contrario, es un Some[URL]. ¡Guay!

Desafortunadamente, Option es probable que tenga cierta sobrecarga porque implica la creación de un objeto adicional. Sin embargo, esa sobrecarga es mínima en comparación con una excepción de puntero nulo.

+0

Para hacer que una clase sea inmutable ¿sugieres hacer una copia de una instancia cada vez que quiera cambiarla? Como dijo Nicolas. – Soteric

+1

Sí, pero esto depende un poco de cómo está usando el objeto. Si tiene un objeto con muchos campos que cambia con frecuencia, es posible que lo inmutable no tenga sentido. Sin embargo, a menudo puede diseñar su código de manera diferente para que realice menos mutación. Si necesita una mutación, puede ocultar la mutabilidad a través de un rasgo (por ejemplo, List tiene un campo mutable; simplemente no puede acceder a él). – schmmd

+0

+1 para el ejemplo del cliente –