Slide 1

Slide 1 text

4DBMBͰͭ͘Δ *OWBMJETUBUFͷͳ͍ੈք :"1$"TJB)BDIJPKJNJEJO4IJOBHBXBTUEBZ

Slide 2

Slide 2 text

ࣗݾ঺հ ͨͬ͘ΜʢUBLLLVOʣ ͲͷαʔϏεͰ΋େମ͜ͷ໊લ 4DBMB 3VCZ &SMBOH

Slide 3

Slide 3 text

ࠓճ͸Կͷ࿩ʁ 4DBMBͰͭ͘Δ *OWBMJETUBUFͷͳ͍ੈք

Slide 4

Slide 4 text

ࠓճ͸Կͷ࿩ʁ 4DBMBͰͭ͘Δ *OWBMJETUBUFͷͳ͍ੈք ˣ όϦσʔγϣϯͷ͓࿩

Slide 5

Slide 5 text

ࠓճ͸Կͷ࿩ʁ 4DBMBͰͭ͘Δ *OWBMJETUBUFͷͳ͍ੈք ˣ όϦσʔγϣϯͷ͓࿩ όϦσʔγϣϯʹࣦഊͨ͠ঢ়ଶΛࢦ͢

Slide 6

Slide 6 text

ൃදʹࢸͬͨܦҢ w ීஈ4DBMBͰΞϓϦέʔγϣϯΞʔΩςΫνϟ͔ Βݕ౼ͭͭ͠։ൃ w ΦϒδΣΫτͷόϦσʔγϣϯʹؔͯ͠΋݁ߏ಄ Λ೧Δ͜ͱ͕͋ͬͨ w ࢼߦࡨޡ͍ͯͨ͠Βྑ͍ײ͡ͷ͕ग़དྷͨ w ౰࣌ʮϠύνʔ΍Δͧʂʯͱ͍͏੠

Slide 7

Slide 7 text

ͳΒ࿩ͦ͏ʂ

Slide 8

Slide 8 text

ΦϒδΣΫτΛόϦσʔγϣϯ͢Δ w ͱ͋ΔΦϒδΣΫτ͕͋Δ w ͦͷΦϒδΣΫτ͸ͻͱͭͳ͍͠͸ෳ਺ͷϑΟʔ ϧυΛ࣋ͭ w ͦͷϑΟʔϧυͷ಺༰೗ԿͰΦϒδΣΫτࣗମ ͷ༗ޮແޮΛܾఆ͢Δ

Slide 9

Slide 9 text

class Person < ActiveRecord::Base validates :name, { presence: true } validates :age, { presence: true, numericality: {greater_than_or_equal_to: 0} } end person = Person.new(name: "takkkun", age: -1) if (person.invalid?) { # handle person.errors } ΦϒδΣΫτΛόϦσʔγϣϯ͢Δ "DUJWF3FDPSEͷྫ

Slide 10

Slide 10 text

ΦϒδΣΫτΛόϦσʔγϣϯ͢Δ 4DBMBͷྫ class Person(val name: String, val age: Int) { def validate(): List[String] = { var errors: List[String] = List.empty if (name.isEmpty) { errors :+= "name cannot be blank" } if (age < 0) { errors :+= "age must be zero or a positive number" } errors } def isValid(): Boolean = validate().isEmpty }

Slide 11

Slide 11 text

ѻ͏ଆ͕όϦσʔγϣϯ࣮ߦͷ੹೚Λ࣋ͭ val person = new Person("takkkun", -1) if (!person.isValid) { val errors = person.validate() // handle errors } ѻ͏ଆ͕όϦσʔγϣϯΛݺͼग़͠ɺؾʹ͔͚Δඞཁ͕͋Δ val person = new Person("takkkun", -1) // handle person όϦσʔγϣϯΛݺͼग़͞ͳ͍ϛε΋ى͜ΓಘΔ def handlePerson(person: Person) { if (person.isValid) { // handle person } } ؔ਺ͳͲͰڥքΛ·͍ͨͩ৔߹΋ؾʹ͔͚Δඞཁ͕͋Δ

Slide 12

Slide 12 text

ΊΜͲ͍͘͞ʂ

Slide 13

Slide 13 text

w ͜ͷΞϓϩʔν͸ʮΦϒδΣΫτ͕༗ޮແޮͱ ͍͏ঢ়ଶʯΛ࣋ͭ w ঢ়ଶΛ࣋ͭҎ্ؾʹ͔͚Δඞཁ͕͋Δ w ·ͨɺ༗ޮແޮҎ֎ʹ΋ঢ়ଶΛ࣋ͭ͜ͱ͸ߟ͑ ΒΕΔ w ͍ΖΜͳঢ়ଶ͕૊Έ߹Θ͞Δͱʜʜ ʮঢ়ଶʯ͸ةݥͳݴ༿

Slide 14

Slide 14 text

ΊΜͲ͍͘͞ʂʂ

Slide 15

Slide 15 text

ʮৗʹ༗ޮʯͱ͍͏ߟ͑ํ w ʮΦϒδΣΫτ͕༗ޮແޮͰ͋Δͱ͍͏ঢ়ଶʯ ͕अຐͳΒແͯ͘͠͠·͓͏ w ͢ͳΘͪʮৗʹ༗ޮʯ w ͜ͷΞϓϩʔνࣗମ͸௝͍͠Θ͚Ͱ΋ͳ͍ ʢྫ೔࣌ΫϥεͳͲʣ

Slide 16

Slide 16 text

ʮৗʹ༗ޮʯͱ͍͏ߟ͑ํ w %%%ͷจ຺ʹ͓͚ΔΤϯςΟςΟͰɺʮৗʹ༗ ޮͳΤϯςΟςΟʯͱͯ͠l"MXBZTWBMJEFOUJUZz ͱ͍͏ݴ༿͕࢖ΘΕ͍ͯΔ w ͦΕʹͳΒ͍ʮৗʹ༗ޮʯ͸l"MXBZTWBMJEzͱশ ͢Δ

Slide 17

Slide 17 text

"MXBZTWBMJE*OWBMJETUBUF͕ͳ͍

Slide 18

Slide 18 text

4DBMBͰͭ͘Δ *OWBMJETUBUFͷͳ͍ੈք

Slide 19

Slide 19 text

ͪͳΈʹ "MXBZTWBMJEΛ൷൑͢Δߟ͑ํ΋͋Δ IUUQKF⒎SFZQBMFSNPDPNCMPHUIFGBMMBDZPG UIFBMXBZTWBMJEFOUJUZ

Slide 20

Slide 20 text

class Person(val name: String, val age: Int) { var errors: List[String] = List.empty if (name.isEmpty) { errors :+= "name cannot be blank" } if (age < 0) { errors :+= "age must be zero or a positive number" } if (errors.nonEmpty) { throw new Exception(errors.mkString(", ")) } } 4DBMBͰ"MXBZTWBMJEΛ࣮ݱ͢Δ

Slide 21

Slide 21 text

class Person(val name: String, val age: Int) { var errors: List[String] = List.empty if (name.isEmpty) { errors :+= "name cannot be blank" } if (age < 0) { errors :+= "age must be zero or a positive number" } if (errors.nonEmpty) { throw new Exception(errors.mkString(", ")) } } 4DBMBͰ"MXBZTWBMJEΛ࣮ݱ͢Δ όϦσʔγϣϯʹࣦഊͨ͠ͱ͖͸ྫ֎ͱ͢Δ

Slide 22

Slide 22 text

"MXBZTWBMJE͸ຬ͍ͨͤͯΔ͕ʜʜ w ίϯετϥΫλͰ͸ࣄ্࣮ྫ֎ͰࣦഊΛ஌Βͤ Δ͔͠ͳ͍ͷͰɺऔΓճ͕͠ޮ͔ͳ͍ʢҰԠ TDBMBVUJM5SZͰͳΜͱ͔ग़དྷΔʣ w όϦσʔγϣϯΛॻ͔ͳ͍ϛε͸๷͛ͳ͍ʢς ετॻ͜͏Ͷʣ w ͳΜͱ͔͸ͳΔ͕ɺͳΜ͔ͩΜͩ৽ͨͳ໰୊͕ ු্ͯ͘͠Δ

Slide 23

Slide 23 text

4DBMBͰ"MXBZTWBMJEΛ࣮ݱ͢Δվ class Person private (val name: String, val age: Int) object Person { def spawn(name: Indefinite[String], age: Indefinite[Int]): Person = { // ... } }

Slide 24

Slide 24 text

4DBMBͰ"MXBZTWBMJEΛ࣮ݱ͢Δվ class Person private (val name: String, val age: Int) object Person { def spawn(name: Indefinite[String], age: Indefinite[Int]): Person = { // ... } } ίϯετϥΫλΛQSJWBUFʹ͢ΔʢΫϥεࣗମ͸QVCMJDʣ

Slide 25

Slide 25 text

4DBMBͰ"MXBZTWBMJEΛ࣮ݱ͢Δվ class Person private (val name: String, val age: Int) object Person { def spawn(name: Indefinite[String], age: Indefinite[Int]): Person = { // ... } } TQBXOϝιουܦ༝ͰͷΈ1FSTPOΫϥεͷΠϯελϯεΛੜ੒Մೳ

Slide 26

Slide 26 text

4DBMBͰ"MXBZTWBMJEΛ࣮ݱ͢Δվ class Person private (val name: String, val age: Int) object Person { def spawn(name: Indefinite[String], age: Indefinite[Int]): Person = { // ... } } ܕΛม͑ͯ௚઀౉ͤͳ͍Α͏ʹ͢Δ

Slide 27

Slide 27 text

class Indefinite[A](value: A) { def validateWith(validateValue: A => List[String]): Validity[A] = { val errors = validateValue(value) if (errors.isEmpty) Valid(value) else Invalid(errors) } } sealed trait Validity[+A] { def value: A def errors: List[String] } case class Valid[A](value: A) extends Validity[A] { val errors = List.empty } case class Invalid(errors: List[String]) extends Validity[Nothing] { def value = throw new NoSuchElementException("Invalid") } 4DBMBͰ"MXBZTWBMJEΛ࣮ݱ͢Δվ

Slide 28

Slide 28 text

class Indefinite[A](value: A) { def validateWith(validateValue: A => List[String]): Validity[A] = { val errors = validateValue(value) if (errors.isEmpty) Valid(value) else Invalid(errors) } } sealed trait Validity[+A] { def value: A def errors: List[String] } case class Valid[A](value: A) extends Validity[A] { val errors = List.empty } case class Invalid(errors: List[String]) extends Validity[Nothing] { def value = throw new NoSuchElementException("Invalid") } ೚ҙͷܕʢ"ʣͷ஋ΛऔΔɻWBMͰम০͍ͯ͠ͳ͍ͷͰɺ֎෦ʹެ։͞Εͳ͍ 4DBMBͰ"MXBZTWBMJEΛ࣮ݱ͢Δվ

Slide 29

Slide 29 text

class Indefinite[A](value: A) { def validateWith(validateValue: A => List[String]): Validity[A] = { val errors = validateValue(value) if (errors.isEmpty) Valid(value) else Invalid(errors) } } sealed trait Validity[+A] { def value: A def errors: List[String] } case class Valid[A](value: A) extends Validity[A] { val errors = List.empty } case class Invalid(errors: List[String]) extends Validity[Nothing] { def value = throw new NoSuchElementException("Invalid") } WBMJEBUF8JUIϝιουͰ಺แ͍ͯ͠Δ஋ͷ༗ޮແޮΛ͔֬ΊΔ 4DBMBͰ"MXBZTWBMJEΛ࣮ݱ͢Δվ

Slide 30

Slide 30 text

4DBMBͰ"MXBZTWBMJEΛ࣮ݱ͢Δվ class Person private (val name: String, val age: Int) object Person { def spawn(name: Indefinite[String], age: Indefinite[Int]): Person = { (name.validateWith(validateName), age.validateWith(validateAge)) match { case (Valid(validName), Valid(validAge)) => new Person(validName, validAge) case (validityName, validityAge) => throw new Exception((validityName.errors ++ validityAge.errors).mkString(", ")) } } private def validateName(name: String): List[String] = { if (name.isEmpty) List("name cannot be blank") else List.empty } private def validateAge(age: Int): List[String] = { if (age < 0) List("age must be zero or a positive number") else List.empty } }

Slide 31

Slide 31 text

4DBMBͰ"MXBZTWBMJEΛ࣮ݱ͢Δվ class Person private (val name: String, val age: Int) object Person { def spawn(name: Indefinite[String], age: Indefinite[Int]): Person = { (name.validateWith(validateName), age.validateWith(validateAge)) match { case (Valid(validName), Valid(validAge)) => new Person(validName, validAge) case (validityName, validityAge) => throw new Exception((validityName.errors ++ validityAge.errors).mkString(", ")) } } private def validateName(name: String): List[String] = { if (name.isEmpty) List("name cannot be blank") else List.empty } private def validateAge(age: Int): List[String] = { if (age < 0) List("age must be zero or a positive number") else List.empty } } WBMJEBUF8JUIʹόϦσʔγϣϯ༻ͷؔ਺Λ ౉͠ɺ಺แ͞Ε͍ͯΔ஋ͷ༗ޮແޮΛಘΔ

Slide 32

Slide 32 text

4DBMBͰ"MXBZTWBMJEΛ࣮ݱ͢Δվ class Person private (val name: String, val age: Int) object Person { def spawn(name: Indefinite[String], age: Indefinite[Int]): Person = { (name.validateWith(validateName), age.validateWith(validateAge)) match { case (Valid(validName), Valid(validAge)) => new Person(validName, validAge) case (validityName, validityAge) => throw new Exception((validityName.errors ++ validityAge.errors).mkString(", ")) } } private def validateName(name: String): List[String] = { if (name.isEmpty) List("name cannot be blank") else List.empty } private def validateAge(age: Int): List[String] = { if (age < 0) List("age must be zero or a positive number") else List.empty } } ύλʔϯϚονͰ͢΂ͯ༗ޮͷͱ͖ͷΈ1FSTPOΫϥεͷΠϯελϯεΛੜ੒͢Δ

Slide 33

Slide 33 text

࢖༻ྫ try { val name = new Indefinite("takkkun") val age = new Indefinite(-1) val person = Person.spawn(name, age) // handle person } catch { case e: Exception => // handle exception }

Slide 34

Slide 34 text

&JUIFSόʔδϣϯ object Person { def spawn(name: Indefinite[String], age: Indefinite[Int]): Either[List[String], Person] = { (name.validateWith(validateName), age.validateWith(validateAge)) match { case (Valid(validName), Valid(validAge)) => Right(new Person(validName, validAge)) case (validityName, validityAge) => Left(validityName.errors ++ validityAge.errors) } } // ... }

Slide 35

Slide 35 text

&JUIFSόʔδϣϯ࢖༻ྫ val name = new Indefinite("takkkun") val age = new Indefinite(-1) val errorsOrPerson = Person.spawn(name, age) errorsOrPerson match { case Left(errors) => // handle errors case Right(person) => // handle person }

Slide 36

Slide 36 text

·ͱΊ w ʮΦϒδΣΫτ͕༗ޮ͔ແޮ͔ͱ͍͏ঢ়ଶʯΛ ࣋ͭͷ͸໘౗͞૿େͷஹީ w ނʹΦϒδΣΫτ͸ৗʹ༗ޮʢ"MXBZTWBMJEʣͰ ͋Εɺͱ͍͏ߟ͑ํ w 4DBMBͳͲͷ੩తܕ෇͚ݴޠͰ͋Ε͹ɺܕͰόϦ σʔγϣϯʹڧ੍ྗΛ࣋ͨͤΒΕΔ

Slide 37

Slide 37 text

ϦϥΫͰ͸։ൃऀΛืू͍ͯ͠·͢ w ීஈ͜͏͍͏͜ͱΛߟ͑ͳ͕Β։ൃ͍ͯ͠·͢ w αʔόʔαΠυ͸4DBMB͕ओ w J04 4XJGU "OESPJE΋ w ΍Δ͜ͱ͍ͬͺ͍͋Δ w ڵຯ͕͋Δํ͸!UBLLLVO·Ͱ

Slide 38

Slide 38 text

͓͠·͍