LoginSignup
1949
1712

More than 5 years have passed since last update.

null安全でない言語は、もはやレガシー言語だ

Last updated at Posted at 2016-11-07

この数年で null 安全 [^99] は一気に浸透したように思います。ざっと思いつくだけでも、次のプログラミング言語で null 安全 が採用されました。

言語 null 安全 に関する公式の記述 リプレース対象言語 オンラインドキュメント ブラウザ上で試す
Ceylon Typesafe null and flow-sensitive typing Java Tour of Ceylon Ceylon Web IDE
Crystal Null Pointer Exception Ruby Crystal Programming Language play.crystal-lang.org
Flow Maybe Types JavaScript Quick Reference Try Flow
Hack Nullable PHP Hack Documentation -
Kotlin Null Safety Java Reference Try Kotlin
Python Union types - The Python Tutorial Interactive Shell
Rust Module std::option C++ The Rust Programming Language Rust Playground
Swift Optionals Objective-C The Swift Programming Language IBM Swift Sandbox
TypeScript Non-nullable Types JavaScript Handbook Playground

 Java  Kotlin  Ceylon  JavaScript  TypeScript  Flow  Objective-C  Swift  PHP  Hack  Python  null   Crystal  Ruby 使 Ruby  null   Rust  C++  Firefox  C++ 使 [^100] 

null  null  GC10C Java, JavaScript, Objective-C, Python  null   使2 null  使 null  

null  使 null   null  

 [^101]  null    null  

稿 null   null    


null 

null 

nullable 

nullable 



null 

FAQ

null 


null   null  nil, None 

null 
// Java
String s = null;
s.length(); // java.lang.NullPointerException
// JavaScript
let s = null;
s.length; // TypeError: Cannot read property 'length' of null
# Python
s = None
len(s) # TypeError: object of type 'NoneType' has no len()

 null   null 
// Kotlin
val s: String? = null
s.length // error: only safe (?.) or non-null asserted (!!.) calls are allow
// TypeScript
let s: string|null = null;
s.length; // Object is possibly 'null'.
# Python
s = None # type: Optional[str]
len(s) # error: Argument 1 to "len" has incompatible type "Optional[str]"; expected "Sized"

 String?  string|null, Optional[str]  ? |null, Optional  s null  nullable  null  length  s.length 

 s s null OK
// Kotlin
val s: String? = null
if (s != null) {
    s.length // OK
}
// TypeScript
let s: string|null = null;
if (s != null) {
    s.length; // OK
}
# Python
s = None # type: Optional[str]
if s is not None:
    len(s) # OK

 if {}  s null  s.length  {}  s String?  String 

 ? |null, Optional  null  null 
// Kotlin
val s: String = null // error: null can not be a value of a non-null type kotlin.String
s.length
// TypeScript
let s: string = null; // Type 'null' is not assignable to type 'string'.
s.length;
# Python
s = None # type: str
# error: Incompatible types in assignment (expression has type None, variable has type "str")
len(s)

 null   ? |null, Optional  non-null [^102]  null  non-null  null  s.length  null 

 nullable  non-null  null  null  

null 


    

 null 

null  Tony Hoare  [^103] 


10null19654010


null  null   null 

null 


 null  null   null  

null   Foo?  nullable  non-null  null  null  null  non-null  null    null  assert  non-null 使 null 

nullable  null  null    null  

 null  


non-null 

nullable 



nullable 


null   nullable  nullable  null  null  null   nullable 便


null 

!, !!

?., ?->

?:, ??

map

flatMap

do 

?




null 使
言語 !, !! ?., ?-> ?:, ?? map flatMap do 記法 ? 型推論
Ceylon
Crystal
Flow
Hack
Haskell
Java
Kotlin
Python
Rust
Scala
Swift
TypeScript


Java  Scala  null   null 使 Optional, Option 使 null   Optional, Option 使

Python  ?   Union[Foo, None]  Optional[Foo]  Foo? 

Java   

null 


 if null  if {}  String?  String   (Smart cast) (Kotlin),  (Control flow analysis) (TypeScript), Flow-sensitive typing (Ceylon) 稿  

  null  s String?  s String?  null  s.length  length  s String 
// Kotlin
val s: String? = null
if (s != null) {
  (s as String).length // String にキャストしてから s を使う
}

しかし、これだともし null チェックを忘れてもコンパイルが通ってしまいます。

// Kotlin
val s: String? = null
(s as String).length // 実行時エラー

s  null  String  nullable  non-null  null 

 null  non-null  null  non-null  null  

 Swift  Rust  if let (Optional binding)  Java  Optional  ifPresent  Haskell  Scala   使   null  non-null 

!, !!


 null  nullable  non-null 

 "123"  123  "ABC"  null  String  Int? 
// Swift
let number: Int? = Int(text)

しかし、テキストフィールドに制約が加えられていて数字しか入力できないこともあります。そのようなケースでは文字列から整数への変換に失敗することはありません。それでも、テキストフィールドから得られるのは文字列なので、型の上では整数への変換が必要となります。

// Swift
if let number: Int = number { // null チェック
  // ここでは number を Int として使える
}

 null  null   null  nullable  null    null  null  nullable  non-null 

Swift  nullable  ! non-null  Forced unwrapping 
// Swift
let number: Int = Int(text)!

number  Int?  Int  ! Int?  Int(text)  Int 

 null  ! non-null  !  null  nullable 使 null  !

 !1 ! null  

TypeScript  ! Kotlin  !! nullable  non-null  Java  Optional  Scala  Option  get  Rust  Option  unwrap 使 null  

 Swift  Forced unwrapping  ! ! Java  get  Map  get 使 Swift  Swift 

?., ?->


nullable  null  null  [^104] 
// Kotlin
val s: String? = null
val length: Int?
if (s != null) {
  length = s.length
} else {
  length = null
}

length を取得するためだけにこの分岐を書くのは大変です。 Swift や Kotlin, Ceylon, Hack などの ?. ( Swift では Optional chaining と呼びます)を使えばこれを簡単に実現できます( Hack では . の代わりに -> を使うので ?-> です)。

// Kotlin
val s: String? = null
val length: Int? = s?.length // s が null なら結果も null

特に、 ?.null になるかもしれない操作をチェーンするときに重宝します。

// Kotlin
val baz: Baz? = foo?.bar?.baz

これを null チェックと条件分岐で書こうとすると次のようになります。

// Kotlin
val baz: Baz?
if (foo != null) {
  val bar: Bar? = foo.bar
  if (bar != null) {
    baz = bar.baz
  } else {
    baz = null
  }
} else {
  baz = null
}

?. によって、 null チェックによる複雑な条件分岐を書かかずに簡潔なコードで済ませられます。

?:, ??

null の場合にデフォルト値を与えたいというのもよくあるケースです。

// Kotlin
val s: String? = null
val length: Int
if (s != null) {
  length = s.length
} else {
  length = 0 // デフォルト値 0 を与える
}

Kotlin の ?: 演算子や Swift の ?? 演算子を使えばこれを簡単に書けます。

// Kotlin
val s: String? = null
val length: Int = s?.length ?: 0 // デフォルト値 0 を与える

Java  Optional  orElse 

map


?.  nullable 

 Int?  ?.  null  null 

 square 
// Swift
let number: Int? = nil
let squared: Int? = number?.square()

しかし * 演算子で number * number のように書こうとすると ?. ではうまく書けません。

// Swift
let number: Int? = nil
let squared: Int? = number?. * number?. // コンパイルエラー

これを null チェックと条件分岐で書くと次のようになります。

// Swift
let number: Int? = nil
let squared: Int?
if let number: Int = number { // null (nil) チェック
  squared = number * number
} else {
  squared = null
}

とても面倒です。こういう場合には、 map という高階メソッドが役立ちます。

// Swift
let number: Int? = nil
let squared: Int? = number.map { $0 * $0 }

Swift  number.map { $0 * $0 }  JavaScript  number.map(function(x) { return x * x; })  number.map(x => x * x)  Java  number.map(x -> x * x) 

map  Optional  nil  map  non-null  Optional  nil  nil 

Java  Optional  Scala, Rust  Option  map  Haskell  Maybe  fmap  Kotlin  nullable  map  ?.  let  number?.let { it * it } 

flatMap



// Swift
let number: Int? = Int(text)

textString ではなく String?だったらどうでしょうか。整数への変換が Int? を返すので、 map で書くと Optional がネストして戻り値の型が Int?? になってしまいます。

// Swift
let text: String? = nil
let number: Int?? = text.map { Int($0) }

Int?? がネストしている部分です。今ほしいのは Int? なのでこのままではダメです。

flatMapmap と似ていますが、ネストした Optional を一重に潰して(フラットにして)返してくれます。

// Swift
let text: String? = null
let number: Int? = text.flatMap { Int($0) }

Java  Optional  Scala  Option  flatMap  Rust  Option  and_then  Haskell  Maybe  >>= 

 map  flatMap  ?.  ?.  map  flatMap  map  flatMap  ?. 

 Kotlin  Ceylon, TypeScript, Python  nullable  Kotlin  Int??  Int?  map  flatMap  Kotlin  flatMap  map  ?.let 

do 


a: Int?  b: Int?  a b a b null  null 

null  flatMap  map 使
// Swift
let a: Int? = 2
let b: Int? = 3
let sum: Int? = a.flatMap { x in b.map { y in x + y } }

a.flatMap { x in b.map { y in x + y } }  JavaScript  a.flatMap(x => b.map(y => x + y))  Java  a.flatMap(x -> b.map(y -> x + y)) 

 Haskell  do使

do  flatMap  map  map  flatMap 
// Swift
let sum: Int? = a.flatMap { x in b.flatMap { y in .some(x + y) } }

.some(x + y) は、 Int 型である x + y の結果を Optional で包んで Int? 型にするための処理です。これを Haskell で書くと次のようになります。

-- Haskell
sum = a >>= (\x -> b >>= (\y -> Just (x + y)))

>>=  Swift  flatMap  Just  .some  do使 do >>= 
-- Haskell
sum = do
  x <- a
  y <- b
  return (x + y)

<-  do nullable  a, b non-null  x, y non-null  x + y  >>=  flatMap 

Scala 
// Scala
val a = Option(2)
val b = Option(3)

val sum = for {
  x <- a
  y <- b
} yield x + y




 ?. 使

 ?.  map  flatMap 使

flatMap  do使


 Haskell  Scala  ?.  Swift  Kotlin  do

?


Swift  Kotlin, Ceylon, Hack, Flow  ? nullable  nullable 

Foo?  Swift  Optional<Foo>  Ceylon  Foo|Null  Kotlin  ? nullable  ? ?便

TypeScript  2.0  Crystal  Crystal 


 null     null  

null   null 使 null   null  

  
// Swift (型推論なし)
let numbers: [Int] = [2, 3, 5]
let squares: [Int] = numbers.map { (x: Int) -> Int in x * x }
// Swift (型推論あり)
let numbers = [2, 3, 5]
let squares = numbers.map { $0 * $0 }
// JavaScript
let numbers = [2, 3, 5];
let squares = numbers.map(x => x * x);

 Swift  JavaScript 

nullable 


 nullable  nullable 




Haskell

Java

Rust

Scala

Swift





Ceylon

Crystal

Hack

Kotlin

Python

TypeScript


nullable  Foo??  Foo?  Foo  null  Foo??  null  Foo? 

Foo??  Map  Dictionary 使

Kotlin  Java  Map<K, V>  K V null  V?  Map<String, Int>  String  Int? 
// Kotlin
val map: Map<String, Int> = mapOf("a" to 2, "b" to 3, "d" to 5)
map["a"] // 2
map["e"] // null (キー "e" に対応した値が存在しない)

 null  Int?  V Map<String, Int?>  V?  V Int?  Int??  Kotlin  ? null  Int??  Int  null  null  Int? 
// Kotlin
val map: Map<String, Int?> = mapOf("a" to 2, "b" to 3, "c" to null, "d" to 5)
map["a"] // 2
map["c"] // null (キー "c" に対応した値 null )
map["e"] // null (キー "e" に対応した値が存在しない)

 null 

 nullable  Swift  Int??  Int? 
// Swift
let map: [String: Int?] = ["a": 2, "b": 3, "c": nil, "d": 5]
map["a"] // Optional(Optional(2))
map["c"] // Optional(nil)
map["e"] // nil

これなら、 nil という値が存在したのか、それとも値が存在しなかったのかで分岐することができます。

// Swift
if let c: Int? = map["c"] {
  if let c: Int = c {
    // nil でない値が存在した場合
  } else {
    // 値 nil が存在した場合
  }
} else {
  // キーに対応した値が存在しなかった場合
}

nullable  nullable  null  便

 nullable  nullable 

nullable  non-null 


 ? nullable  non-null  Kotlin  Hack  Foo  ? Foo?  Hack  ?Foo  Foo?  Foo  null 
// Kotlin
val s1: String = null // コンパイルエラー
val s2: String? = null // OK

 Foo??  nullable  Foo  null  null  Foo  null  Foo?  Foo??  Foo? 

nullable 便 Kotlin  Kotlin  "100% interoperable with Java"  Java  Java  null  Java 

Union type


Ceylon  TypeScript, Python, Crystal  Union type 使 nullable  Union Type  Foo|Bar  Foo  Bar  Python  Union[Foo, Bar]

Ceylon  Foo|Null, TypeScript  Foo|null, Python  Union[Foo, None]  nullable  Ceylon  null  Null  TypeScript  null  null  Python  None  None 
// Ceylon
Foo|Null foo = null;
// TypeScript
let foo: Foo|null = null;
# Python
foo = None # type: Union[Foo, None]

Foo|Null  Foo  null  Union type  Union type  nullable 

 Ceylon  Python  Foo|Null  Union[Foo, None]  Ceylon  Foo?  Foo|Null  Python  Optional[Foo]  Union[Foo, None] 
// Ceylon
Foo? foo = null;
# Python
foo = None # type: Optional[Foo]

TypeScript  string|null  nullable  Crystal  Crystal 

Union type  nullable  Kotlin  Foo|Null|Null (Foo??)  Foo  Null  Null  Foo|Null (Foo?)  Foo  Null  Foo?? == (Foo?)? == (Foo|Null)|Null == Foo|Null|Null == Foo|Null == Foo? 

Tagged union


Swift  Rust, Haskell  Swift  Foo?  nullable  some  none  Union type  Foo?  some(Foo)|none  Union  Tagged union 

nullable  Swift  Optional  Haskell  Maybe 
// Swift
enum Optional<T> {
  case none
  case some(T)
}
-- haskell
data Maybe a = Nothing | Just a

Swift  none  some  Haskell  Nothing  Just 

 nullable  Foo?? == (Foo?)? == some(some(Foo)|none)|none  ? none  ? none 

Union type  Tagged union  nullable  Tagged union  Tagged union  nullable 


Java  Optional  Scala  Option 

Java  Optional  null  Scala  Option  Some  None  Scala 

Java  Optional  isPresent 
// Java
public boolean isPresent() {
  return value != null;
}

しかし、 Scala の OptionisEmptySomeNone でそれぞれ実装されています。

// Scala
final case class Some[+A](x: A) extends Option[A] {
  def isEmpty = false
}
case object None extends Option[Nothing] {
  def isEmpty = true
}

false, true  Scala  isEmpty  map  flatMap  [^105]  [^106] 

 Java  Optional  Scala  Option  Java  sealed class  Java  Optional  Some  None  Optional  final  Some  None  Optional  Scala  sealed class  Some  None  Option 

 Optional  Optional<Optional<Foo>> 

:  @kmizu  Scala  Kotlin  sealed class  Tagged union  Scala  Option  Tagged union 


 null  


C#

C++

Crystal

Go

Java

JavaScript

Kotlin

Objective-C

Python

Ruby

Scala

Swift

TypeScript

C#


C#  T? (Nullable<T>) 稿 nullable  T nullable  T non-null  null  

 C#  null   TypeScript  null  

: @gorn708 


C#
https://github.com/dotnet/roslyn/blob/features/NullableReferenceTypes/docs/features/NullableReferenceTypes/Nullable%20reference%20types.md


 Proposal  C#  null  

C++


C++17  std::optional  null   std::optional  C++  null  

Crystal


 Ruby  Ruby  Ruby  1.0 

Go


Go10 null   

GoGo

 Kotlin  nullable  non-null GoGo

Java


Java 8  Optional  Java  nullable  Optional  null  Map  get  null  null   nullable  Java  Optional 使 [^107]  null 

 JSR 305 [^108]  non-null  @NonNull  JSR 305  @NonNull  @NonNull 稿

JavaScript


 [^109] [^110] [^111]  null   JavaScript  null   TypeScript  Flow 使

Kotlin


 Swift  Objective-C  Objective-C  Objective-C  null   Kotlin  Java  null   Java  @NonNull 

Objective-C


Objective-C  nil  nil 
Foo *foo = nil;
NSString *bar = foo.bar; // OK: bar == nil

しかし、これは null 安全 ということではありません。いつでも nil が実行時エラーを起こさないわけではなく、この bar を次のように使うと実行時エラーとなります。

NSURL *url = [[NSURL alloc] initWithString: bar]; // 'NSInvalidArgumentException', reason: '*** -[NSURL initWithString:relativeToURL:]: nil string parameter'

Objective-C  nil  Objective-C  Objective-C  Java  Java  NullPointerException  Objective-C  nil 

Swift  Swift 使 Objective-C 使 C++  Swift 使

Python


Python  [^116]  3.5 使 Python 2  null  
# Python 3
def to_int(s: str) -> Optional[int]:
# Python 2
def to_int(s): # type: (str) -> Optional[int]

型ヒント やコメント構文で記述された型は mypy [^112] [^113] を使ってチェックします。 mypy は Python の静的型チェッカーです。 Python 2 でも使える [^114] ということは、すべての Python プログラマが今すぐでも null 安全 なコードを書けるということです。

なお、 mypy は現状ではデフォルトで null 安全 なモードになっていないので、 null 安全 にするには

mypy --strict-optional foo.py

 --strict-optional  [^115] 

Python  Optional  Proposal  PEP 505 [^117] 

Ruby


 +  Ruby  RubyKaigi 2016  Matz  [^118]  null   Ruby 3  null  

Scala


 Scala  Scala  null 


ScalanullOptionnull使JavaScalanull使
  (@kmizu) 2016101

Scala  null   null 使 Option  null  

Swift


 nullable 使 Swift  Objective-C  null   Objective-C  null   Apple 

TypeScript


TypeScript 2.0  null   null     --strictNullChecks  [^104]

null   Python  ?.  JavaScript  [^119] 

null 


null  使 null   null  

 null   使 null  使
言語 null 安全 な言語の導入しやすさ
Java
JavaScript
Objective-C
Python
Ruby

ただし、マークの意味は次の通りです。

マーク 意味
既存コードに対して付加的であり、問題が起こってもすぐに元に戻すことができる。導入のための障害はほぼ存在しない。
既存コードとシームレスに連携することができる。ただし、新しい言語で書いたコードは新しい言語としてメンテする必要はある。新しい言語は言語仕様が変化しがちなので、メンテコストは既存コードより相対的に高くなることが多い。
既存のコードと連携することはできない。使い捨てのスクリプトや試験的なプロジェクトなど利用できるケースが限定的。

Java


Java  null   Ceylon, Kotlin, Scala  Kotlin 

Kotlin  Java  Java 使 Ceylon  Scala 

Kotlin  Java  Java  Kotlin 使 Kotlin 

 Android  Kotlin  Android Studio  IntelliJ IDEA  Kotlin  IntelliJ IDEA  JetBrains  Android Studio  Java  [^120] [^121]

JavaScript


AltJS  TypeScript  Flow 使 TypeScript  Flow 使 @mizchi  [^122] 


使flowtypetypescriptflowtypeJSrails


TypeScript  Flow  JavaScript  TypeScript  Flow  JavaScript 使

 TypeScript 2.0  null   null   --strictNullChecks  --strictNullChecks  [^104]

Objective-C


Objective-C  null   Swift  Swift  Swift  Swift 3  Swift  Swift  Swift  Objective-C 使

Swift  Objective-C  Objective-C  Swift  Objective-C 使 Objective-C  Swift  Xcode 使

Python


mypy [^112]  Python  null   Python 2 使 [^114]  Python 3  Python 2 使

mypy  Python  Gradual Typing 


[] Python  mypy! [^113]

What is Gradual Typing:  [^123]

Mypy syntax cheat sheet [^124]

PEP 484 -- Type Hints [^116]


 mypy  null   --strict-optional  [^115] 

Ruby


 Ruby  Ruby  Crystal  Ruby  Ruby  Crystal 

 Crystal  Crystal  Amethyst [^125]  Rails  Web 

FAQ


 Twitter  @omochimetaru 

Q. null 


A. GC

Q. null 


A. 使 null   nullable  ?: 

Q. null 


A. null  null 58 null  Integer  0  1

Q. 


A. null    

Q. null 


A.  null  null  nullable 

Q. Option 


A. 稿

Q. ?.  null 


A. ?.  null   ?.  Groovy  CoffeeScript, Dart  null   null  ?.  null  

[^99]: null   null   null safety  null safety  Kotlin 使 Optional  Option, Maybe, nullable type  null  null  稿 null  

[^100]: Shipping Rust in Firefox

[^101]: 

[^102]: non-nullable  TypeScript 稿 Kotlin  non-null 

[^103]:  - Wikipedia

[^104]: Kotlin  if使使

[^105]: Option.scala - scala/scala

[^106]: Optional.kt - qoncept/kotopt: Kotlin  sealed class 使 Some  None 

[^107]: OptionalSerializable使 - 

[^108]: JSR 305: Annotations for Software Defect Detection

[^109]: Proposal for types in Harmony

[^110]: Types (Experimental) - google/traceur-compiler

[^111]: ES8 Proposal: Optional Static Typing

[^112]: mypy

[^113]: [] Python  mypy!

[^114]: mypy: Type checking Python 2 code

[^115]: mypy: Experimental strict optional type and None checking

[^116]: PEP 484 -- Type Hints

[^117]: PEP 505 -- None-aware operators

[^118]: RubyKaigi 2016 調 Ruby3 Typing

[^119]: Suggestion: "safe navigation operator", i.e. x?.y 16 - Microsoft/TypeScript

[^120]: Getting started with Android and Kotlin

[^121]: AndroidKotlin使

[^122]: flowtype

[^123]: What is Gradual Typing: 

[^124]: Mypy syntax cheat sheet

[^125]: Codcore/amethyst: Crystal  Rails
1949
1712
31

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

1949
1712