Usi di Null / Nothing / Unit in Scala


95

Ho appena letto: http://oldfashionedsoftware.com/2008/08/20/a-post-about-nothing/

Per quanto ho capito, Nullè un tratto e la sua unica istanza lo è null.

Quando un metodo accetta un argomento Null, allora possiamo passargli solo un Nullriferimento o nulldirettamente, ma non qualsiasi altro riferimento, anche se è nullo ( nullString: String = nullad esempio).

Mi chiedo solo in quali casi l'utilizzo di questo Nulltratto potrebbe essere utile. C'è anche il tratto Niente per il quale non vedo più esempi.


Non capisco nemmeno qual è la differenza tra l'utilizzo di Nothing e Unit come tipo di ritorno, poiché entrambi non restituiscono alcun risultato, come sapere quale usare quando ho un metodo che esegue la registrazione, ad esempio?


Hai utilizzi di Unit / Null / Nothing come qualcos'altro che un tipo di ritorno?

Risposte:


80

Si utilizza Nothing solo se il metodo non restituisce mai (il che significa che non può essere completato normalmente restituendo, potrebbe generare un'eccezione). Niente è mai istanziato ed è lì a beneficio del sistema dei tipi (per citare James Iry: "Il motivo per cui Scala ha un tipo inferiore è legato alla sua capacità di esprimere la varianza nei parametri di tipo." ). Dall'articolo a cui ti sei collegato:

Un altro utilizzo di Nothing è come tipo restituito per metodi che non restituiscono mai. Ha senso se ci pensi. Se il tipo restituito da un metodo è Nothing e non esiste assolutamente alcuna istanza di Nothing, tale metodo non deve mai restituire.

Il tuo metodo di registrazione restituirà Unit. Esiste un valore Unità quindi può effettivamente essere restituito. Dai documenti dell'API :

L'unità è un sottotipo di scala.AnyVal. Esiste un solo valore di tipo Unit, () e non è rappresentato da alcun oggetto nel sistema di runtime sottostante. Un metodo con tipo restituito Unit è analogo a un metodo Java dichiarato void.


2
Grazie, con "non ritorna mai" intendi che la chiamata si blocca a tempo indeterminato (ad esempio il metodo di avvio del job scheduler?)
Sebastien Lorber

3
@ Sabastien: non ritorna normalmente, potrebbe lanciare un'eccezione (vedi james-iry.blogspot.com/2009/08/… ). se la chiamata di blocco termina solo con il lancio di un'eccezione, allora conterà. grazie per la domanda, questo doveva essere chiarito.
Nathan Hughes

18

L'articolo che citi può essere fuorviante. Il Nulltipo è lì per compatibilità con la macchina virtuale Java e Java in particolare.

Bisogna considerare che Scala :

  • è completamente orientato agli oggetti: ogni valore è un oggetto
  • è fortemente tipizzato: ogni valore deve avere un tipo
  • deve gestire i nullriferimenti per accedere, ad esempio, alle librerie e al codice Java

diventa quindi necessario definire un tipo per il nullvalore, che è il Nulltratto, e ha nullcome unica istanza.

Non c'è niente di particolarmente utile nel Nulltipo a meno che tu non sia il sistema di tipi o che tu stia sviluppando sul compilatore. In particolare non vedo alcun motivo sensato per definire un Nullparametro di tipo per un metodo, dal momento che non puoi passare altro chenull


è vero, quindi alla fine non c'è nessun altro utilizzo di Null?
Sebastien Lorber

@SebastienLorber ha modificato la risposta. In realtà non riesco a vedere alcun utilizzo per lo sviluppatore medio. Forse qualcun altro può pensare a qualcosa di utile.
pagoda_5b

Grazie per questo. Se conosciamo il motivo di questo genere di cose, le capiamo, altrimenti le ricordiamo.
Sreekar,

Null è utile quando hai un parametro di tipo e potresti voler restituire null come in questa domanda e risposta , dal momento che sei scoraggiato dall'usare null in scala ma raramente viene fuori, potrebbero esserci anche altri usi nel sistema di tipo
Daniel Carlsson

15

Hai usi di Unit / Null / Nothing come qualcos'altro che un tipo di ritorno?


Unit può essere usato in questo modo:

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

Ciò consente di passare un blocco arbitrario di codice da eseguire.


Nullpotrebbe essere utilizzato come tipo inferiore per qualsiasi valore annullabile. Un esempio è questo:

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

Nothing è utilizzato nella definizione di None

object None extends Option[Nothing]

Questo ti permette di assegnare un Nonea qualsiasi tipo di Optionperché Nothing'estende' tutto.

val x:Option[String] = None

Ok. Per il tuo utilizzo di Unit potresti aver usato un tipo generico in modo che l'esecuzione possa restituire questo tipo generico se il tuo blocco di codice restituisce qualcos'altro rispetto a unit.
Sebastien Lorber

Come ha detto @drexin in un commento su un'altra risposta, è usato principalmente per denotare un effetto collaterale.
EECOLOR

6

se usi Nothing, non c'è niente da fare (includi la console di stampa) se fai qualcosa, usa il tipo di outputUnit

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

... allora come si usa Nothing?

trait Option[E]
case class Some[E](value: E) extends Option[E]
case object None extends Option[Nothing]

1
Inoltre, Ein Optiondeve essere in posizione covariante: trait Option[+E]per consentire cose comeval x: Option[Int] = None
vim

5

In realtà non ho mai usato il Nulltipo, ma tu usi Unit, dove faresti su java void. Nothingè un tipo speciale, perché come ha già detto Nathan, non può esserci alcuna istanza di Nothing. Nothingè un cosiddetto tipo di fondo, il che significa che è un sottotipo di qualsiasi altro tipo. Questo (e il parametro di tipo controvariante) è il motivo per cui puoi anteporre qualsiasi valore a Nil- che è a List[Nothing]- e l'elenco sarà quindi di questo tipo di elementi. Noneanche se di tipo Option[Nothing]. Ogni tentativo di accedere ai valori all'interno di un tale contenitore genererà un'eccezione, perché è l'unico modo valido per tornare da un metodo di tipo Nothing.


grazie non sapevo che Nessuno estende l'opzione [Niente]. Ha senso in alcuni usi generici in cui un sottotipo potrebbe usare Nothing (immagino sia difficile trovare un esempio per Null e Unit ...)
Sebastien Lorber,

Viene utilizzata l'unità, dove si verificano effetti collaterali, ad esempio la monade IO può essere del tipo IO[Unit]per la stampa sulla console e simili.
drexin

Sì, non ho mai usato la monade IO, ha senso usarla con Unit (e forse Niente se è qualche operazione IO che produce un flusso infinito?)
Sebastien Lorber,

No, niente non ha senso lì.
drexin

3

Niente è spesso usato implicitamente. Nel codice seguente, val b: Boolean = if (1 > 2) false else throw new RuntimeException("error") la clausola else è di tipo Nothing , che è una sottoclasse di Boolean (così come qualsiasi altra AnyVal). Pertanto, l'intera assegnazione è valida per il compilatore, sebbene la clausola else in realtà non restituisca nulla.


1

Ecco un esempio di Nothingda scala.predef:

  def ??? : Nothing = throw new NotImplementedError

Nel caso in cui tu non abbia familiarità (e i motori di ricerca non possono cercare su di esso) ???è la funzione segnaposto di Scala per tutto ciò che non è stato ancora implementato. Proprio come quello di Kotlin TODO.

È possibile utilizzare lo stesso trucco durante la creazione di oggetti fittizi: sostituire i metodi inutilizzati con un notUsedmetodo personalizzato . Il vantaggio di non utilizzare ???è che non riceverai avvisi di compilazione per cose che non intendi mai implementare.


0

In termini di teoria delle categorie Niente è un oggetto iniziale e Unità è un oggetto terminale .

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

Gli oggetti iniziali sono anche chiamati coterminali o universali e gli oggetti terminali sono anche chiamati finali .

Se un oggetto è sia iniziale che terminale , viene chiamato oggetto zero o oggetto nullo .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.