Usos de Nulo / Nada / Unidad en Scala

95

Acabo de leer: http://oldfashionedsoftware.com/2008/08/20/a-post-about-nothing/

Por lo que tengo entendido, Nulles un rasgo y su única instancia lo es null.

Cuando un método toma un argumento Null, entonces solo podemos pasarle una Nullreferencia o nulldirectamente, pero no cualquier otra referencia, incluso si es nula ( nullString: String = nullpor ejemplo).

Me pregunto en qué casos Nullpodría resultar útil utilizar este rasgo. También está el rasgo Nada para el que realmente no veo más ejemplos.


Realmente tampoco entiendo cuál es la diferencia entre usar Nothing y Unit como tipo de retorno, ya que ambos no devuelven ningún resultado, ¿cómo saber cuál usar cuando tengo un método que realiza el registro, por ejemplo?


¿Tiene usos de Unit / Null / Nothing como algo más que un tipo de retorno?

Sebastien Lorber
fuente

Respuestas:

80

Solo usa Nothing si el método nunca regresa (lo que significa que no puede completarse normalmente al regresar, podría generar una excepción). Nunca se crea una instancia de nada y está ahí para el beneficio del sistema de tipos (para citar a James Iry: "La razón por la que Scala tiene un tipo de fondo está ligada a su capacidad para expresar la variación en los parámetros de tipo" ). Del artículo al que vinculó:

Otro uso de Nothing es como tipo de retorno para métodos que nunca regresan. Tiene sentido si lo piensas. Si el tipo de retorno de un método es Nothing y no existe absolutamente ninguna instancia de Nothing, dicho método nunca debe regresar.

Su método de registro devolvería Unit. Hay una unidad de valor para que realmente se pueda devolver. De los documentos de la API :

Unit es un subtipo de scala.AnyVal. Solo hay un valor de tipo Unidad, (), y no está representado por ningún objeto en el sistema de ejecución subyacente. Un método con el tipo de retorno Unit es análogo a un método Java que se declara nulo.

Nathan Hughes
fuente
2
Gracias, por "nunca regresa", ¿quiere decir que la llamada se bloquea indefinidamente (por ejemplo, el método de inicio del programador de trabajos?)
Sebastien Lorber
3
@Sabastien: no regresa normalmente, podría lanzar una excepción (ver james-iry.blogspot.com/2009/08/… ). si la llamada de bloqueo solo termina lanzando una excepción, eso contará. gracias por la pregunta, esto necesitaba una aclaración.
Nathan Hughes
18

El artículo que cita puede ser engañoso. El Nulltipo está ahí por compatibilidad con la máquina virtual Java , y Java en particular.

Debemos considerar que Scala :

  • está completamente orientado a objetos: cada valor es un objeto
  • está fuertemente tipado: cada valor debe tener un tipo
  • necesita manejar nullreferencias para acceder, por ejemplo, bibliotecas y código Java

por tanto, se hace necesario definir un tipo para el nullvalor, que es el Nullrasgo, y tiene nullcomo única instancia.

No hay nada especialmente útil en el Nulltipo a menos que sea el sistema de tipos o esté desarrollando en el compilador. En particular, no veo ninguna razón sensata para definir un Nullparámetro de tipo para un método, ya que no puede pasar nada más quenull

pagoda_5b
fuente
eso es cierto, entonces al final no hay otro uso de Null?
Sebastien Lorber
@SebastienLorber editó la respuesta. En realidad, no puedo ver ningún uso para el desarrollador promedio. Quizás alguien más pueda pensar en algo útil.
pagoda_5b
Gracias por esto. Si conocemos la razón de este tipo de cosas, las entendemos, de lo contrario las recordamos.
Sreekar
Null es útil cuando tiene un parámetro de tipo y es posible que desee devolver un valor nulo como en esta pregunta y respuesta , ya que no se recomienda usar null en scala; sin embargo, rara vez aparece, también puede haber otros usos en el sistema de tipos
Daniel Carlsson
15

¿Tiene usos de Unit / Null / Nothing como algo más que un tipo de retorno?


Unit se puede utilizar así:

def execute(code: => Unit):Unit = {
  // do something before
  code
  // do something after
}

Esto le permite pasar un bloque arbitrario de código para su ejecución.


Nullpodría usarse como un tipo inferior para cualquier valor que sea anulable. Un ejemplo es este:

implicit def zeroNull[B >: Null] =
    new Zero[B] { def apply = null }

Nothing se utiliza en la definición de None

object None extends Option[Nothing]

Esto le permite asignar un Nonea cualquier tipo de Optionporque Nothing'extiende' todo.

val x:Option[String] = None
EECOLOR
fuente
Okay. Para su uso de Unit, podría haber usado un tipo genérico para que la ejecución pueda devolver este tipo genérico si su bloque de código devuelve algo más que unit.
Sebastien Lorber
Como dijo @drexin en un comentario sobre otra respuesta, se usa principalmente para denotar un efecto secundario.
EECOLOR
6

si lo usa Nothing, no hay cosas que hacer (incluya la consola de impresión) si hace algo, use el tipo de salidaUnit

object Run extends App {
  //def sayHello(): Nothing = println("hello?")
  def sayHello(): Unit = println("hello?")
  sayHello()
}

... entonces, ¿cómo usarlo Nothing?

trait Option[E]
case class Some[E](value: E) extends Option[E]
case object None extends Option[Nothing]
Curycu
fuente
1
Además, Een el Optiontiene que estar en posición covariante: trait Option[+E]para permitir cosas comoval x: Option[Int] = None
vim
5

En realidad, nunca he usado el Nulltipo, pero usas Unit, donde lo harías con java void. Nothinges un tipo especial, porque como Nathan ya mencionó, no puede haber ninguna instancia de Nothing. Nothinges un tipo de fondo, lo que significa que es un subtipo de cualquier otro tipo. Esto (y el parámetro de tipo contravariante) es la razón por la que puede anteponer cualquier valor a Nil, que es a List[Nothing], y la lista será de este tipo de elementos. Nonetambién si de tipo Option[Nothing]. Cada intento de acceder a los valores dentro de dicho contenedor generará una excepción, porque es la única forma válida de regresar de un método de tipo Nothing.

drexin
fuente
gracias No sabía que Ninguno extiende Opción [Nada]. Tiene sentido en el uso de algunos genéricos donde un subtipo podría usar Nothing (supongo que es difícil encontrar un ejemplo para Null y Unit ...)
Sebastien Lorber
Se utiliza la unidad, cuando ocurren efectos secundarios, por ejemplo, la mónada IO puede ser del tipo IO[Unit]para imprimir en la consola y similares.
drexin
Sí, nunca usé la mónada IO, tiene sentido usarla con Unit (y tal vez Nada si se trata de una operación IO que produce un flujo infinito)
Sebastien Lorber
No, nada tiene sentido ahí.
drexin
3

A menudo, nada se usa implícitamente. En el código siguiente, val b: Boolean = if (1 > 2) false else throw new RuntimeException("error") la cláusula else es de tipo Nothing , que es una subclase de Boolean (así como cualquier otro AnyVal). Por lo tanto, toda la asignación es válida para el compilador, aunque la cláusula else realmente no devuelve nada.

Fang Zhang
fuente
1

Aquí está un ejemplo de Nothingpartir scala.predef:

  def ??? : Nothing = throw new NotImplementedError

En caso de que no esté familiarizado (y los motores de búsqueda no puedan buscar en él), ???Scala tiene la función de marcador de posición para cualquier cosa que aún no se haya implementado. Como el de Kotlin TODO.

Puede usar el mismo truco al crear objetos simulados: anule los métodos no utilizados con un notUsedmétodo personalizado . La ventaja de no usar ???es que no recibirá advertencias de compilación para cosas que nunca pretendió implementar.

David Leppik
fuente
0

En términos de teoría de categorías, Nothing es un objeto inicial y Unit es un objeto terminal .

https://en.wikipedia.org/wiki/Initial_and_terminal_objects

Los objetos iniciales también se denominan coterminales o universales , y los objetos terminales también se denominan finales .

Si un objeto es tanto inicial como terminal , se denomina objeto cero u objeto nulo .

Joe
fuente