Option [T] 클래스의 요점은 무엇입니까?
Option[T]
스칼라에서 수업 의 요점을 이해할 수 없습니다 . 내 말은, 나는 None
over의 어떤 이점도 볼 수 없다는 것을 의미 null
합니다.
예를 들어 다음 코드를 고려하십시오.
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 */
}
}
}
이제 방법, 가정 getPerson1
반환 null
후 전화가 만든 display
의 첫 번째 줄에 main
실패 할 수밖에 없다 NPE
. 마찬가지로 getPerson2
returns None
이면 display
유사한 오류와 함께 호출이 다시 실패합니다.
그렇다면 왜 Scala Option[T]
는 Java에서 사용되는 간단한 접근 방식을 따르는 대신 새로운 값 래퍼 ( )를 도입하여 문제를 복잡하게 만들 까요?
최신 정보:
@Mitch 의 제안에 따라 코드를 편집했습니다 . 나는 여전히의 특별한 이점을 볼 수 없습니다 Option[T]
. 예외적 인 경우 null
또는 None
두 경우 모두 테스트해야합니다 . :(
@Michael의 회신 에서 올바르게 이해했다면 유일한 장점은 이 메서드가 None을 반환 할 수Option[T]
있음을 프로그래머에게 명시 적으로 알려주는 것입니다 . 이것이이 디자인 선택의 유일한 이유입니까?
Option
절대 .NET을 절대로 사용하지 않도록 강요하면 더 나은 점을 얻을 수 있습니다 get
. 그 이유 get
는 "ok, 나를 null-land로 돌려 보내줘"와 동일 하기 때문 입니다.
그래서 당신의 예를 들어보십시오. display
사용하지 않고 어떻게 전화 get
하겠습니까? 다음은 몇 가지 대안입니다.
getPerson2 foreach (_.display)
for (person <- getPerson2) person.display
getPerson2 match {
case Some(person) => person.display
case _ =>
}
getPerson2.getOrElse(Person("Unknown", 0)).display
이 대안 중 display
어떤 것도 존재하지 않는 것을 호출하도록 허용 하지 않습니다.
왜 get
존재 하는지에 대해 Scala는 코드 작성 방법을 알려주지 않습니다. 그것은 당신을 부드럽게 자극 할 수 있지만, 안전망이없는 상태로 돌아가고 싶다면 그것은 당신의 선택입니다.
여기에 못 박았습니다.
Option [T]의 유일한 장점은이 메서드가 None을 반환 할 수 있다는 것을 프로그래머에게 명시 적으로 알려주는 것입니다.
"전용"을 제외하고. 그러나 다른 방식으로 다시 말씀 드리겠습니다 . over 의 주요 이점 은 형식 안전성입니다. 컴파일러가 허용하지 않기 때문에 존재하지 않을 수있는 객체에 메서드를 보내지 않도록합니다.Option[T]
T
T
두 경우 모두 null 허용 여부를 테스트해야한다고 말했지만 잊어 버렸거나 모르는 경우 null을 확인해야합니다. 컴파일러가 알려줄까요? 아니면 사용자가?
물론 Java와의 상호 운용성 때문에 Scala는 Java와 마찬가지로 null을 허용합니다. 따라서 Java 라이브러리를 사용하거나 잘못 작성된 Scala 라이브러리를 사용하거나 잘못 작성된 개인 Scala 라이브러리 를 사용하는 경우 여전히 널 포인터를 처리해야합니다.
Option
제가 생각할 수있는 다른 두 가지 중요한 이점 은 다음과 같습니다.
문서화 : 메소드 유형 서명은 객체가 항상 반환되는지 여부를 알려줍니다.
모나 딕 구성 가능성.
후자는 완전히 이해하는 데 훨씬 더 오래 걸리며 복잡한 코드에서만 강점을 보여주기 때문에 간단한 예제에는 적합하지 않습니다. 그래서 아래에 예를 들어 보 겠지만, 이미받은 사람들을 제외하고는 거의 의미가 없다는 것을 잘 알고 있습니다.
for {
person <- getUsers
email <- person.getEmail // Assuming getEmail returns Option[String]
} yield (person, email)
비교:
val p = getPerson1 // a potentially null Person
val favouriteColour = if (p == null) p.favouriteColour else null
와:
val p = getPerson2 // an Option[Person]
val favouriteColour = p.map(_.favouriteColour)
스칼라에 맵 함수 로 나타나는 모나 딕 속성 bind를 사용하면 객체가 'null'인지 여부에 대한 걱정없이 객체에 대한 작업을 연결할 수 있습니다.
이 간단한 예를 좀 더 살펴 보겠습니다. 사람들 목록에서 가장 좋아하는 색상을 모두 찾고 싶다고 가정 해 보겠습니다.
// 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
또는 아버지의 어머니의 누이의 이름을 찾고 싶을 수도 있습니다.
// 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)
나는 이것이 옵션이 어떻게 삶을 좀 더 쉽게 만들 수 있는지에 대한 약간의 빛을 비추 길 바랍니다.
The difference is subtle. Keep in mind to be truly a function it must return a value - null is not really considered to be a "normal return value" in that sense, more a bottom type/nothing.
But, in a practical sense, when you call a function that optionally returns something, you would do:
getPerson2 match {
case Some(person) => //handle a person
case None => //handle nothing
}
Granted, you can do something similar with null - but this makes the semantics of calling getPerson2
obvious by virtue of the fact it returns Option[Person]
(a nice practical thing, other than relying on someone reading the doc and getting an NPE because they don't read the doc).
I will try and dig up a functional programmer who can give a stricter answer than I can.
For me options are really interesting when handled with for comprehension syntax. Taking synesso preceding example:
// 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
If any of the assignation are None
, the fathersMothersSister
will be None
but no NullPointerException
will be raised. You can then safely pass fathersMothersSister
to a function taking Option parameters without worrying. so you don't check for null and you don't care of exceptions. Compare this to the java version presented in synesso example.
You have pretty powerful composition capabilities with Option:
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") )
Maybe someone else pointed this out, but I didn't see it:
One advantage of pattern-matching with Option[T] vs. null checking is that Option is a sealed class, so the Scala compiler will issue a warning if you neglect to code either the Some or the None case. There is a compiler flag to the compiler that will turn warnings into errors. So it's possible to prevent the failure to handle the "doesn't exist" case at compile time rather than at runtime. This is an enormous advantage over the use of the null value.
It's not there to help avoid a null check, it's there to force a null check. The point becomes clear when your class has 10 fields, two of which could be null. And your system has 50 other similar classes. In the Java world, you try to prevent NPEs on those fields using some combination of mental horesepower, naming convention, or maybe even annotations. And every Java dev fails at this to a significant degree. The Option class not only makes "nullable" values visually clear to any developers trying to understand the code, but allows the compiler to enforce this previously unspoken contract.
[ copied from this comment by Daniel Spiewak ]
If the only way to use
Option
were to pattern match in order to get values out, then yes, I agree that it doesn’t improve at all over null. However, you’re missing a *huge* class of its functionality. The only compelling reason to useOption
is if you’re using its higher-order utility functions. Effectively, you need to be using its monadic nature. For example (assuming a certain amount of API trimming):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
There, wasn’t that nifty? We can actually do a lot better if we use
for
-comprehensions:val value = for { row <- database fetchRowById 42 key <- row get "port_key" value <- myMap get key } yield value val result = value getOrElse defaultValue
You’ll notice that we are *never* checking explicitly for null, None or any of its ilk. The whole point of Option is to avoid any of that checking. You just string computations along and move down the line until you *really* need to get a value out. At that point, you can decide whether or not you want to do explicit checking (which you should never have to do), provide a default value, throw an exception, etc.
I never, ever do any explicit matching against
Option
, and I know a lot of other Scala developers who are in the same boat. David Pollak mentioned to me just the other day that he uses such explicit matching onOption
(orBox
, in the case of Lift) as a sign that the developer who wrote the code doesn’t fully understand the language and its standard library.I don’t mean to be a troll hammer, but you really need to look at how language features are *actually* used in practice before you bash them as useless. I absolutely agree that Option is quite uncompelling as *you* used it, but you’re not using it the way it was designed.
One point that nobody else here seems to have raised is that while you can have a null reference, there is a distinction introduced by Option.
That is you can have Option[Option[A]]
, which would be inhabited by None
, Some(None)
and Some(Some(a))
where a
is one of the usual inhabitants of A
. This means that if you have some kind of container, and want to be able to store null pointers in it, and get them out, you need to pass back some extra boolean value to know if you actually got a value out. Warts like this abound in the java containers APIs and some lock-free variants can't even provide them.
null
is a one-off construction, it doesn't compose with itself, it is only available for reference types, and it forces you to reason in a non-total fashion.
For instance, when you check
if (x == null) ...
else x.foo()
you have to carry around in your head throughout the else
branch that x != null
and that this has already been checked. However, when using something like option
x match {
case None => ...
case Some(y) => y.foo
}
you know y is not None
by construction -- and you'd know it wasn't null
either, if it weren't for Hoare's billion dollar mistake.
Option[T] is a monad, which is really useful when you using high-order functions to manipulate values.
I'll suggest you read articles listed below, they are really good articles that show you why Option[T] is useful and how can it be used in functional way.
Adding on to Randall's teaser of an answer, understanding why the potential absence of a value is represented by Option
requires understanding what Option
shares with many other types in Scala—specifically, types modeling monads. If one represents the absence of a value with null, that absence-presence distinction can't participate in the contracts shared by the other monadic types.
If you don't know what monads are, or if you don't notice how they're represented in Scala's library, you won't see what Option
plays along with, and you can't see what you're missing out on. There are many benefits to using Option
instead of null that would be noteworthy even in the absence of any monad concept (I discuss some of them in the "Cost of Option / Some vs null" scala-user mailing list thread here), but talking about it isolation is kind of like talking about a particular linked list implementation's iterator type, wondering why it's necessary, all the while missing out on the more general container/iterator/algorithm interface. There's a broader interface at work here too, and Option
provides a presence-and-absence model of that interface.
I think the key is found in Synesso's answer: Option is not primarily useful as a cumbersome alias for null, but as a full-fledged object that can then help you out with your logic.
The problem with null is that it is the lack of an object. It has no methods that might help you deal with it (though as a language designer you can add increasingly long lists of features to your language that emulate an object if you really feel like it).
One thing Option can do, as you've demonstrated, is to emulate null; you then have to test for the extraordinary value "None" instead of the extraordinary value "null". If you forget, in either case, bad things will happen. Option does make it less likely to happen by accident, since you have to type "get" (which should remind you that it might be null, er, I mean None), but this is a small benefit in exchange for an extra wrapper object.
Where Option really starts to show its power is helping you deal with the concept of I-wanted-something-but-I-don't-actually-have-one.
Let's consider some things you might want to do with things that might be null.
Maybe you want to set a default value if you have a null. Let's compare Java and Scala:
String s = (input==null) ? "(undefined)" : input;
val s = input getOrElse "(undefined)"
In place of a somewhat cumbersome ?: construct we have a method that deals with the idea of "use a default value if I'm null". This cleans up your code a little bit.
Maybe you want to create a new object only if you have a real value. Compare:
File f = (filename==null) ? null : new File(filename);
val f = filename map (new File(_))
Scala is slightly shorter and again avoids sources of error. Then consider the cumulative benefit when you need to chain things together as shown in the examples by Synesso, Daniel, and paradigmatic.
It isn't a vast improvement, but if you add everything up, it's well worth it everywhere save very high-performance code (where you want to avoid even the tiny overhead of creating the Some(x) wrapper object).
The match usage isn't really that helpful on its own except as a device to alert you about the null/None case. When it is really helpful is when you start chaining it, e.g., if you have a list of options:
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.")
}
Now you get to fold the None cases and the List-is-empty cases all together in one handy statement that pulls out exactly the value you want.
Null return values are only present for compatibility with Java. You should not use them otherwise.
It is really a programming style question. Using Functional Java, or by writing your own helper methods, you could have your Option functionality but not abandon the Java language:
http://functionaljava.org/examples/#Option.bind
Just because Scala includes it by default doesn't make it special. Most aspects of functional languages are available in that library and it can coexist nicely with other Java code. Just as you can choose to program Scala with nulls you can choose to program Java without them.
Admitting in advance that it is a glib answer, Option is a monad.
Actually I share the doubt with you. About Option it really bothers me that 1) there is a performance overhead, as there is a lor of "Some" wrappers created everywehre. 2) I have to use a lot of Some and Option in my code.
So to see advantages and disadvantages of this language design decision we should take into consideration alternatives. As Java just ignores the problem of nullability, it's not an alternative. The actual alternative provides Fantom programming language. There are nullable and non-nullable types there and ?. ?: operators instead of Scala's map/flatMap/getOrElse. I see the following bullets in the comparison:
Option's advantage:
- simpler language - no additional language constructs required
- uniform with other monadic types
Nullable's advantage:
- shorter syntax in typical cases
- better performance (as you don't need to create new Option objects and lambdas for map, flatMap)
So there is no obvious winner here. And one more note. There is no principal syntactic advantage for using Option. You can define something like:
def nullableMap[T](value: T, f: T => T) = if (value == null) null else f(value)
Or use some implicit conversions to get pritty syntax with dots.
The real advantage of having explicit option types is that you are able to not use them in 98% of all places, and thus statically preclude null exceptions. (And in the other 2% the type system reminds you to check properly when you actually access them.)
Another situation where Option works, is in situations where types are not able to have a null value. It is not possible to store null in an Int, Float, Double, etc. value, but with an Option you can use the None.
In Java, you would need to use the boxed versions (Integer, ...) of those types.
참고URL : https://stackoverflow.com/questions/2079170/what-is-the-point-of-the-class-optiont
'programing tip' 카테고리의 다른 글
Prototype.js를 사용한 JSON.stringify () 배열 기괴함 (0) | 2020.09.24 |
---|---|
C #에서 null이 아닌 경우 메서드 호출 (0) | 2020.09.24 |
Javascript를 사용하여 특정 날짜의 에포크 얻기 (0) | 2020.09.24 |
내`INSERT… ON DUPLICATE KEY UPDATE`에서 2 개의 행이 영향을받는 이유는 무엇입니까? (0) | 2020.09.24 |
프로덕션에서 바벨 노드를 사용해도 되나요? (0) | 2020.09.24 |