Zastosowania wartości Null / Nothing / Unit w Scali

95

Właśnie przeczytałem: http://oldfashionedsoftware.com/2008/08/20/a-post-about-nothing/

O ile rozumiem, Nulljest to cecha i jej jedynym przykładem jest null.

Kiedy metoda przyjmuje argument o wartości Null, wówczas możemy przekazać jej tylko Nullreferencję lub nullbezpośrednio, ale nie inne odwołanie, nawet jeśli ma wartość null ( nullString: String = nullna przykład).

Zastanawiam się tylko, w jakich przypadkach użycie tej Nullcechy może być przydatne. Jest też cecha Nic, dla której nie widzę więcej przykładów.


Nie bardzo rozumiem też, jaka jest różnica między używaniem Nothing i Unit jako typu zwracanego, ponieważ oba nie zwracają żadnego wyniku, jak sprawdzić, którego użyć, gdy mam na przykład metodę, która wykonuje rejestrowanie?


Czy używasz Unit / Null / Nothing jako czegoś innego niż typ zwracany?

Sebastien Lorber
źródło

Odpowiedzi:

80

Używasz Nothing tylko wtedy, gdy metoda nigdy nie zwraca (co oznacza, że ​​nie może zakończyć normalnie zwracając, może zgłosić wyjątek). Nic nigdy nie jest tworzone i istnieje z korzyścią dla systemu typów (cytując Jamesa Iry'ego: „Powód, dla którego Scala ma typ dolny jest związany z jego zdolnością do wyrażania wariancji parametrów typu” ). Z artykułu, do którego utworzyłeś link:

Jeszcze jedno użycie Nothing jest typem zwracanym dla metod, które nigdy nie zwracają. Jeśli o tym pomyślisz, ma to sens. Jeśli zwracanym typem metody jest Nothing i nie ma absolutnie żadnego wystąpienia Nothing, to taka metoda nie może nigdy zwrócić.

Twoja metoda logowania zwróciłaby Unit. Istnieje wartość Jednostka, więc można ją faktycznie zwrócić. Z dokumentacji API :

Jednostka jest podtypem scala.AnyVal. Istnieje tylko jedna wartość typu Unit, () i nie jest reprezentowana przez żaden obiekt w bazowym systemie wykonawczym. Metoda ze zwracanym typem Unit jest analogiczna do metody Java, która jest uznana za nieważną.

Nathan Hughes
źródło
2
Dzięki, czy przez „nigdy nie zwraca” masz na myśli to, że połączenie jest blokowane na czas nieokreślony (na przykład metoda uruchamiania harmonogramu zadań?)
Sebastien Lorber
3
@Sabastien: nie wraca normalnie, może zgłosić wyjątek (patrz james-iry.blogspot.com/2009/08/… ). jeśli wywołanie blokujące kończy się tylko rzuceniem wyjątku, to by się liczyło. dzięki za pytanie, to wymagało wyjaśnienia.
Nathan Hughes
18

Cytowany artykuł może wprowadzać w błąd. NullTyp jest tam za zgodność z maszyną wirtualną Java i Java w szczególności.

Musimy wziąć pod uwagę, że Scala :

  • jest całkowicie zorientowany obiektowo: każda wartość jest obiektem
  • jest silnie wpisany: każda wartość musi mieć typ
  • musi obsługiwać nullodwołania, aby uzyskać dostęp, na przykład, do bibliotek i kodu Java

w związku z tym konieczne staje się zdefiniowanie typu nullwartości, która jest Nullcechą i ma nulljako jedyny egzemplarz.

W Nulltypie nie ma nic szczególnie przydatnego, chyba że jesteś systemem typów lub programujesz na kompilatorze. W szczególności nie widzę żadnego sensownego powodu do definiowania Nullparametru typu dla metody, ponieważ nie można przekazać niczego innegonull

pagoda_5b
źródło
to prawda, więc ostatecznie nie ma innego zastosowania Null?
Sebastien Lorber
@SebastienLorber zredagował odpowiedź. W rzeczywistości nie widzę żadnego zastosowania dla przeciętnego programisty. Może ktoś inny wymyśli coś pożytecznego.
pagoda_5b
Dzięki za to. Jeśli znamy przyczynę tego rodzaju rzeczy, rozumiemy je, w przeciwnym razie pamiętamy.
Sreekar
Wartość null jest przydatna, gdy masz parametr typu i możesz chcieć zwrócić wartość null, jak w tym pytaniu i odpowiedzi , ponieważ zniechęcasz do używania wartości null w scali, ale rzadko się pojawia, mogą być również inne zastosowania w systemie typów
Daniel Carlsson
15

Czy używasz Unit / Null / Nothing jako czegoś innego niż typ zwracany?


Unit można używać w ten sposób:

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

Pozwala to na przekazanie dowolnego bloku kodu do wykonania.


Nullmoże być używany jako typ dolny dla dowolnej wartości, której wartość dopuszcza wartość null. Oto przykład:

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

Nothing jest używany w definicji None

object None extends Option[Nothing]

Pozwala to przypisać a Nonedo dowolnego typu, Optionponieważ Nothing„rozszerza” wszystko.

val x:Option[String] = None
EECOLOR
źródło
Dobrze. Do korzystania z Unit można było użyć typu ogólnego, aby funkcja wykonywalna mogła zwrócić ten typ ogólny, jeśli blok kodu zwraca coś innego niż jednostka.
Sebastien Lorber
Jak @drexin powiedział w komentarzu do innej odpowiedzi, jest używany głównie do oznaczenia efektu ubocznego.
EECOLOR
6

jeśli używasz Nothing, nie ma nic do zrobienia (w tym konsoli drukowania), jeśli coś robisz, użyj typu wyjściaUnit

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

... więc jak używać Nothing?

trait Option[E]
case class Some[E](value: E) extends Option[E]
case object None extends Option[Nothing]
Curycu
źródło
1
Poza Etym Optionmusi być w kowariantnej pozycji: trait Option[+E]aby pozwolić na rzeczy takie jakval x: Option[Int] = None
vim
5

Właściwie nigdy nie użyłem tego Nulltypu, ale używasz tego Unit, czego używałbyś w Javie void. Nothingjest szczególnym typem, ponieważ jak już wspomniał Nathan, nie może być instancji Nothing. Nothingjest tzw. typem dolnym, co oznacza, że ​​jest podtypem dowolnego innego typu. To (i parametr typu kontrawariantnego) jest powodem, dla którego możesz poprzedzić dowolną wartość Nil- która jest List[Nothing]- a lista będzie wtedy tego typu elementów. Nonetakże jeśli jest typu Option[Nothing]. Każda próba uzyskania dostępu do wartości wewnątrz takiego kontenera spowoduje zgłoszenie wyjątku, ponieważ jest to jedyny prawidłowy sposób powrotu z metody typu Nothing.

drexin
źródło
dzięki Nie wiedziałem, że Żaden nie rozszerza opcji [Nic]. Ma to sens w niektórych rodzajach użycia, w których podtyp może używać Nothing (myślę, że trudno jest znaleźć przykład dla Null i Unit ...)
Sebastien Lorber
Jednostka jest używana, gdy występują efekty uboczne, na przykład monada IO może być typu IO[Unit]do drukowania na konsoli i tym podobnych.
drexin
Tak, nigdy nie używany IO monada ma sens używać go z jednostki (i prawdopodobnie nic, jeśli nie jest to jakaś operacja IO która produkuje nieskończoną strumienia?)
Sebastien LORBER
Nie, tam nic nie ma sensu.
drexin
3

Nic nie jest często używane w sposób dorozumiany. W poniższym kodzie inny klauzula jest typu Nothing , który jest podklasą Boolean (jak również wszelkie inne AnyVal). Zatem całe przypisanie jest poprawne dla kompilatora, chociaż klauzula else tak naprawdę nic nie zwraca.val b: Boolean = if (1 > 2) false else throw new RuntimeException("error")

Fang Zhang
źródło
1

Oto przykład Nothingz scala.predef:

  def ??? : Nothing = throw new NotImplementedError

W przypadku, gdy nie jesteś zaznajomiony (a wyszukiwarki nie mogą w tym wyszukiwać) ???, funkcja zastępcza Scala dla wszystkiego, co nie zostało jeszcze zaimplementowane. Tak jak Kotlin TODO.

Możesz użyć tej samej sztuczki podczas tworzenia pozorowanych obiektów: nadpisać nieużywane metody notUsedmetodą niestandardową . Zaletą nieużywania ???jest to, że nie otrzymasz ostrzeżeń o kompilacji dla rzeczy, których nigdy nie zamierzasz zaimplementować.

David Leppik
źródło
0

W kategoriach teorii kategorii Nic nie jest przedmiotem początkowym, a Jednostka jest obiektem końcowym .

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

Obiekty początkowe są również nazywane koterminalami lub uniwersalnymi , a obiekty końcowe nazywane są również ostatecznymi .

Jeśli obiekt jest zarówno początkowy, jak i końcowy , nazywany jest obiektem zerowym lub zerowym .

Joe
źródło