Extensible Exception
kbkz.tech #10
吉村 優
https://twitter.com/_yyu_
http://qiita.com/yyu
https://github.com/y-yu
July 16, 2016
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 1 / 30
自己紹介
筑波大学 情報科学類 学士
(COINS11)
現在は Scala を書く仕事に従事
エラー処理に関する話をします
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 2 / 30
エラー値とは?
エラー値
エラーであることを表す値
しばしば階層構造(木構造)になる
エラー値の階層構造の例
RootException
FileException
WriteExceptionReadException
DbExceptionHttpException
どうやって階層構造を作る?
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 3 / 30
継承を用いた表現
RootException
FileException
WriteExceptionReadException
DbExceptionHttpException
trait RootException extends Throwable
case class DbException(m: String) extends RootException
case class HttpException(m: String) extends RootException
trait FileException extends RootException
case class ReadException(m: String) extends FileException
case class WriteException(m: String) extends FileException
どうして継承を使うの?
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 4 / 30
サブタイプ多相
“
型 A が型 B の subtype(部分型)のとき、型 B の式を書く
べきところに、型 A の式を書いても良い。
筑波大学 言語論 [1]
”RootException
FileException
WriteExceptionReadException
DbExceptionHttpException
例
RootException を書くべきところに、DbException を書く
FileException を書くべきところに、WriteException を書く
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 5 / 30
Either
trait Either[+A, +B] {
def flatMap[AA >: A, Y](f: B => Either[AA, Y]) =
this match {
case Left(a) => Left(a)
case Right(b) => f(b)
}
}
case class Left [+A, +B](a: A) extends Either[A, B]
case class Right[+A, +B](b: B) extends Either[A, B]
flatMapの型パラメータで AA >: A を取る
AA >: A は AA が A のスーパータイプであることを表す
// Left[FileException]
for {
a <- Left(WriteException("file write error"))
b <- Left(ReadException("file read error"))
} yield ()
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 6 / 30
Either
for {
a <- Left(HttpException("http error"))
b <- Left(DbException("db error"))
} yield ()
これの型は Left[RootException]になる
RootException
FileException
WriteExceptionReadException
DbExceptionHttpException
型の上では FileException と区別できなくなった!
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 7 / 30
階層構造の拡張
RootException
FileException
WriteExceptionReadException
DbExceptionHttpException
後からこの階層構造を変更できる?
RootException
FileException
WriteExceptionReadException
InternalServerErrorException
DbExceptionHttpException
でも既存のコードは修正したくない……
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 8 / 30
階層構造の拡張
無理では?
継承でやるのはよくない?
型クラスでやろう!
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 9 / 30
階層構造の拡張
1 新しい型クラス:~>(Transform)を導入
2 新しいエラー値を定義
3 型クラス:~>のインスタンスを定義
4 型クラス:~>のインスタンスを使うように Either を拡張
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 10 / 30
新しい型クラス:~>の定義
変換を表す型クラス:~>
型 A から型 B への変換ができることを表す型クラス
trait :~>[-A, +B] {
def apply(a: A): B
}
例
Intから Stringへの変換ができることを表すインスタンス
implicit val intToString = new (Int :~> String) {
def apply(a: Int): String = a.toString
}
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 11 / 30
新しいエラー値の定義
case class InternalServerErrorException(m: String)
extends RootException
継承に基づく階層構造
RootException
FileException
Read&WriteException
DbExceptionHttpExceptionInternalServerErrorException
この時点で、HttpException と DbException は
InternalServerErrorException と関係がない
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 12 / 30
型クラス:~>のインスタンス定義
object InternalServerErrorException {
implicit val databaseException =
new (DbException :~> InternalServerErrorException) {
def apply(a: DbException): InternalServerErrorException =
InternalServerErrorException(s"database: ${a.m}")
}
implicit val httpException =
new (HttpException :~> InternalServerErrorException) {
def apply(a: HttpException): InternalServerErrorException =
InternalServerErrorException(s"http: ${a.m}")
}
}
次のインスタンスを定義する
DbException から InternalServerErrorException への変換
HttpException から InternalServerErrorException への変換
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 13 / 30
型クラス:~>のインスタンス定義
継承に基づく階層構造とインスタンス
RootException
FileException
Read&WriteException
DbExceptionHttpExceptionInternalServerErrorException
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 14 / 30
Eitherの拡張
既存の Either
trait Either[+A, +B] {
def flatMap[AA >: A, Y](f: B => Either[AA, Y]) =
this match {
case Left(a) => Left(a)
case Right(b) => f(b)
}
}
case class Left [+A, +B](a: A) extends Either[A, B]
case class Right[+A, +B](b: B) extends Either[A, B]
Pimp my Library パターンで Either を拡張
implicit class ExceptionEither[L1, R1](val ee: Either[L1, R1]) {
???
}
mapと flatMapを拡張
新しいメソッド asを導入
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 15 / 30
mapとflatMapの拡張
def map[L2, R2](f: R1 => R2)
(implicit F: L1 :~> L2): Either[L2, R2] =
ee match {
case Left(e) => Left(F(e))
case Right(v) => Right(f(v))
}
def flatMap[L2, R2](f: R1 => Either[L2, R2])
(implicit F: L1 :~> L2): Either[L2, R2] =
ee match {
case Left(e) => Left(F(e))
case Right(v) => f(v)
}
型クラス:~>のインスタンスを implicit パラメータで検索
Leftの場合、型クラス:~>のインスタンスを用いて変換
Rightの場合は元の Either と同様
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 16 / 30
新しいメソッドasの導入
def as[L2](implicit F: L1 :~> L2): Either[L2, R1] =
ee match {
case Left(e) => Left(F(e))
case Right(v) => Right(v)
}
型クラス:~>のインスタンスを implicit パラメータで検索
型クラス:~>のインスタンスを用いて変換
これの何が便利なの?
後で使います
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 17 / 30
例
import InternalServerErrorException._
val e1 = Left(DbException("db error"))
val e2 = Left(HttpException("http error"))
// Left[InternalServerErrorException]
for {
a <- e1
b <- e2.as[InternalServerErrorException]
} yield ()
抽象的な型へ変換できた!
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 18 / 30
既存の階層構造との互換性
自らへのインスタンスがない
val e1 = Left(DbException("db error"))
// compile time error!
for {
a <- e1
} yield ()
継承関係との互換性がない
val e3 = Left(WriteException("file write error"))
val e4 = Left(ReadException("file read error"))
// compile time error!
for {
a <- e3
b <- e4.as[FileException]
} yield ()
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 19 / 30
自明なインスタンスの導入
self(自らへのインスタンス)
implicit def self[A] = new (A :~> A) {
def apply(a: A): A = a
}
superclass(スーパータイプへのインスタンス)
implicit def superclass[A, B >: A] = new (A :~> B) {
def apply(a: A): B = a
}
これだけで OK?
もうひとつ必要!
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 20 / 30
Transitive
A :~> Bと B :~> Cから A :~> Cというインスタンスを生成
A B
C
Transitive?
implicit def transitive[A, B, C]
(implicit F: A :~> B, G: B :~> C): A :~> C = new (A :~> C) {
def apply(a: A): C = G(F(a))
}
Compile Time Error
diverging implicit expansion for type :~>[HttpException,B]
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 21 / 30
ワンステップの変換を表す型クラスの定義
:->
trait :->[-A, +B] {
def apply(a: A): B
}
型クラス:->(Transform1)は推移を含まないワンステップ
の変換を表す
型クラス:->のインターフェースは:~>と全く同じ
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 22 / 30
:~>のインスタンスを:->に変更
Before
object InternalServerErrorException {
implicit val databaseException =
new (DbException :~> InternalServerErrorException) {
def apply(a: DbException): InternalServerErrorException =
InternalServerErrorException(s"database: ${a.m}")
}
implicit val httpException =
new (HttpException :~> InternalServerErrorException) {
def apply(a: HttpException): InternalServerErrorException =
InternalServerErrorException(s"http: ${a.m}")
}
}
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 23 / 30
:~>のインスタンスを:->に変更
After
object InternalServerErrorException {
implicit val databaseException =
new (DbException :-> InternalServerErrorException) {
def apply(a: DbException): InternalServerErrorException =
InternalServerErrorException(s"database: ${a.m}")
}
implicit val httpException =
new (HttpException :-> InternalServerErrorException) {
def apply(a: HttpException): InternalServerErrorException =
InternalServerErrorException(s"http: ${a.m}")
}
}
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 24 / 30
Transitiveの定義
Transitive
implicit def transitive[A, B, C]
(implicit F: A :-> B, G: B :~> C): A :~> C = new (A :~> C) {
def apply(a: A): C = G(F(a))
}
ワンステップの変換:->を使って発散を防止
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 25 / 30
例
RootException
File
WriteRead
DbHttpInternalServerErrorControllerError
import InternalServerErrorException._
import ControllerErrorException._
// Left[ControllerErrorException]
for {
a <- Left(DatabaseException("db error"))
b <- Left(HttpException("http error"))
c <- Left(ReadException("file read error"))
.as[ControllerErrorException]
} yield ()
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 26 / 30
まとめ
継承によるエラーの階層構造は後からの変更が困難になる
変換を表す型クラスを利用することで、この問題を解決で
きる
ワンステップの変換を表す型クラスを別に用意することで、
推移を扱うことができる
Haskell や Rust にもこの方法を導入できるかもしれない
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 27 / 30
参考文献
亀山幸義.
プログラム言語論 オブジェクト指向, 2015.
吉村優.
階層構造を容易に拡張できる例外, 2015.
吉村優.
The missing method of extensible exception: implicit
“transitive”, 2016.
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 28 / 30
目次
1 エラー値とは?
2 サブタイピングと Either
3 階層構造の拡張
4 新しい型クラスと Either の拡張
5 自明なインスタンスの導入
6 Transitive
7 まとめ
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 29 / 30
Thank you for listening!
Any question?
吉村 優 (https://twitter.com/_yyu_) Extensible Exception July 16, 2016 30 / 30

More Related Content

PDF
Swift 2.0 で変わったところ「前編」 #cswift
PDF
リテラルと型の話 #__swift__
PDF
Freer Monads, More Extensible Effects
PPTX
大人のお型付け
PDF
Keiya hgraph
PDF
Tatsuya hgraph2
PDF
El dolor
PDF
Sara 304
Swift 2.0 で変わったところ「前編」 #cswift
リテラルと型の話 #__swift__
Freer Monads, More Extensible Effects
大人のお型付け
Keiya hgraph
Tatsuya hgraph2
El dolor
Sara 304

Viewers also liked (8)

PDF
Keiya hgraph3
PDF
Graph
PDF
Scaling the Agile Organisation
PDF
多相な関数の定義から学ぶ、型クラスデザインパターン
PDF
CISI Cyprus National Advisory Council
PDF
чорне море фото-слайди
DOCX
Paddy Power -Sarah Fay
PPTX
London Analysis
Keiya hgraph3
Graph
Scaling the Agile Organisation
多相な関数の定義から学ぶ、型クラスデザインパターン
CISI Cyprus National Advisory Council
чорне море фото-слайди
Paddy Power -Sarah Fay
London Analysis
Ad

Similar to Extensible Exception (20)

PDF
Implicit Explicit Scala
PDF
Phantom Type in Scala
PPT
オブジェクト指向入門10
PPTX
GoF デザインパターン 2009
PPTX
Introduction to Functional Programming
PDF
例外設計における大罪
PDF
Scalaで型クラス入門
PPT
オブジェクト指向入門9
PDF
続・ゲンバのSwift
PDF
オブジェクト指向開発におけるObject-Functional Programming
PDF
Object-oriented Programming / Exception handling
ODP
これから Haskell を書くにあたって
PDF
Scalaプログラミング・マニアックス
PPTX
Xtend の紹介
PPTX
スーパークラスとインターフェース 共通する機能を実装するためにどちらがよいのか(若手プログラマーのオブジェクト指向の疑問に答えていく)
PDF
Phpではじめるオブジェクト指向(公開用)
PDF
Scalaによる型安全なエラーハンドリング
PDF
Scalaでの例外処理
PDF
演算子オーバーライドをDSLに活用する
PDF
Monadicプログラミング マニアックス
Implicit Explicit Scala
Phantom Type in Scala
オブジェクト指向入門10
GoF デザインパターン 2009
Introduction to Functional Programming
例外設計における大罪
Scalaで型クラス入門
オブジェクト指向入門9
続・ゲンバのSwift
オブジェクト指向開発におけるObject-Functional Programming
Object-oriented Programming / Exception handling
これから Haskell を書くにあたって
Scalaプログラミング・マニアックス
Xtend の紹介
スーパークラスとインターフェース 共通する機能を実装するためにどちらがよいのか(若手プログラマーのオブジェクト指向の疑問に答えていく)
Phpではじめるオブジェクト指向(公開用)
Scalaによる型安全なエラーハンドリング
Scalaでの例外処理
演算子オーバーライドをDSLに活用する
Monadicプログラミング マニアックス
Ad

More from Hikaru Yoshimura (7)

PDF
Fujitask meets Extensible Effects
PDF
BitcoinでCTF
PDF
公平なガチャ(tsukuba.lua)
PDF
Minimal Cake Pattern in Swift
PDF
Mental Jinroを支える暗号技術
PDF
Regular expressions à la carte
PPTX
Tagless Final DSL
Fujitask meets Extensible Effects
BitcoinでCTF
公平なガチャ(tsukuba.lua)
Minimal Cake Pattern in Swift
Mental Jinroを支える暗号技術
Regular expressions à la carte
Tagless Final DSL

Extensible Exception