SlideShare a Scribd company logo
Type Level Programming in scala
@liam.m
1
Requirements
— Scala? .
— !
— !
— !
— !
2
?
!
—
—
—
—
—
—
—
3
?
4
Once your code compiles it usually works.2
.
2 
https://wiki.haskell.org/Why_Haskell_just_works
5
TDD
TDD - Type driven development3
3 
https://www.manning.com/books/type-driven-development-with-idris
6
1. Type safe equality - ===
2. Builder pattern using Phantom type
3. Type class pattern - implicit
4. Literal type - 42.type
5. Dependent type - a.B
Type .
7
Equality?
val items = List(
Item(1, Some("ON_SALE")), Item(2, Some("SOLDOUT")),
Item(3, Some("ON_SALE")), Item(4, None))
items.filter(item => item.status == "ON_SALE")
?
8
Equality?
val items = List(
Item(1, Some("ON_SALE")), Item(2, Some("SOLDOUT")),
Item(3, Some("ON_SALE")), Item(4, None))
items.filter(item => item.status == "ON_SALE")
Nil .
item.status: Option[String] != "ON_SALE": String
9
.
!
test("item status filter") {
val expected = List(
Item(1, Some("ON_SALE")),
Item(3, Some("ON_SALE"))
)
assert(items == expected) // !
}
10
.
!
test("item status filter") {
val expected = List(
Item(1, Some("ON_SALE")),
Item(3, Some("ON_SALE"))
)
assert(items == expected) // !
}
?
11
.
!
test("item status filter") {
val expected = List(
Item(1, Some("ON_SALE")),
Item(3, Some("ON_SALE"))
)
assert(items == expected) // !
}
?
Once your code compiles it usually works.
12
Type safe equality
implicit class StrictEq[A](a: A) {
def ===(b: A) = a == b
}
13
Type safe equality
implicit class StrictEq[A](a: A) {
def ===(b: A) = a == b
}
implicit class .
A ===
14
COMPILE TIME .
val x = Some("ON_SALE")
val y = Some("SOLDOUT")
val z = "ON_SALE"
x === y // false
x === z // doesn't compile,
.
⛑
15
=== .
...
16
scalacOptions += "-Xfatal-warnings"
case class UserId(n: Int)
val john = UserId(7)
john == 7
// <console>:11: warning: comparing values of
// types UserId and Int using `=='
// will always yield false john == 7
warning -> error
17
18
19
Builder pa!ern
"The builder pattern is a good choice when designing
classes whose constructors or static factories would
have more than a handful of parameters."
Joshua Bloch, Effective Java
!
20
Builder pa!ern
"The builder pattern is a good choice when designing
classes whose constructors or static factories would
have more than a handful of parameters."
Joshua Bloch, Effective Java
!
21
// , .
public class Item {
private Integer id;
private Integer brandId;
private Integer catalogId;
private Integer supplyChannelId; // optional
private String supplyChannelItemCode;
private String supplyChannelCategoryCode;
private String name;
private String displayName;
private Integer itemType;
private Integer basicPrice;
private Integer sellingPrice;
private Integer discountRate;
private Integer feeRate;
private Byte periodType;
private Integer validPeriod;
private Byte pinIssueType;
private String pinIssueCsInfo; // optional
private Boolean isCancelable;
private String imageUrl; // optional
private Integer status;
private Boolean displayYn;
private Short distType; // optional
private String detailInfo; // optional
private String noticeInfo; // optional
private Instant couponExpiredAt; // optional
private Instant releasedAt;
private Instant expiredAt;
... // 51
}
?
22
- Joshua Bloch
public class Item {
public static class Builder {
// Builder
public Builder(Integer id, Integer brandId, Integer catalogId, String supplyChannelItemCode,
String supplyChannelCategoryCode, String name, String displayName, Integer itemType,
Integer basicPrice, Integer sellingPrice, Integer discountRate, Integer feeRate,
Byte periodType, Integer validPeriod, String pinIssueCsInfo, Byte pinIssueType,
Boolean isCancelable, Integer status, Boolean displayYn, Instant releasedAt, Instant expiredAt) {
this.id = id; this.brandId = brandId; this.catalogId = catalogId;
this.supplyChannelItemCode = supplyChannelItemCode;
this.supplyChannelCategoryCode = supplyChannelCategoryCode;
this.name = name; this.displayName = displayName; this.itemType = itemType;
this.basicPrice = basicPrice; this.sellingPrice = sellingPrice;
this.discountRate = discountRate; this.feeRate = feeRate;
this.periodType = periodType; this.validPeriod = validPeriod;
this.pinIssueType = pinIssueType; this.pinIssueCsInfo = pinIssueCsInfo;
this.isCancelable = isCancelable; this.status = status;
this.displayYn = displayYn; this.releasedAt = releasedAt; this.expiredAt = expiredAt;
}
//
public Builder withSupplyChannelId(Integer val) {
this.supplyChannelId = val;
return this;
}
public Builder withPinIssueCsInfo(String val) {
this.pinIssueCsInfo = val;
return this;
}
...
}
}
23
Builder builder .
Item item = new Item.Builder(
10,
1000,
2000,
"I123",
"C123",
"( ) ",
" ",
101,
5000,
3000,
40,
5,
(byte)2,
365,
"CS",
(byte) 3,
true,
201,
true,
Instant.now(),
Instant.MAX
).withNoticeInfo(" ").withImageUrl("http://builder.jpg")
.build();
24
: Builder builder
— Builder withXXX
25
/* Item.scala */
case class Item(
id: Int,
brandId: Int,
catalogId: Int,
supplyChannelId: Option[Int], //
supplyChannelItemCode: String, // .
supplyChannelCategoryCode: String,
name: String,
displayName: String,
itemType: Int,
basicPrice: Int,
sellingPrice: Int,
discountRate: Int,
feeRate: Int,
periodType: Byte,
validPeriod: Int,
pinIssueType: Byte,
pinIssueCsInfo: Option[String],
isCancelable: Boolean,
imageUrl: Option[String],
status: Int,
displayYn: Boolean,
distType: Option[Short],
detailInfo: Option[String],
noticeInfo: Option[String],
couponExpiredAt: Option[Instant],
releasedAt: Instant,
expiredAt: Instant
)
26
type .
27
type .
Why?
28
type .
Why? Compiler type type
29
type .
Why? Compiler type type
// .
sealed trait BuildState {
type Id <: Bool // True
type Name <: Bool // True
}
30
sealed trait Bool
val True = new Bool {}
val False = new Bool {}
val hasOption: Bool = True
31
sealed trait Bool
sealed trait True extends Bool
sealed trait False extends Bool
type HasOption = True
32
— ADT Values : val → trait
val True = new Bool {} ➜ trait True extends Bool
33
— ADT Values : val → trait
val True = new Bool {} ➜ trait True extends Bool
— members : val → type X
val hasOption: Bool = True ➜ type HasOption = True
34
sealed trait Bool
//
val True = new Bool {}
val False = new Bool {}
val hasOption: Bool = True
//
sealed trait True extends Bool
sealed trait False extends Bool
type HasOption = True
35
Name, DisplayName True
class Builder[B <: BuildState] { self =>
private var name: Option[String] = None
private var displayName: Option[String] = None
def newBuilder[C <: BuildState] = this.asInstanceOf[Builder[C]]
def withName(name: String) = {
self.name = Some(name)
newBuilder[B {type Name = True}]
}
def withDisplayName(displayName: String) = {
self.displayName = Some(displayName)
newBuilder[B {type DisplayName = True}]
}
}
36
True
class Builder[B <: BuildState] { self =>
def build(implicit
ev1: B#Id =:= True, // True
ev2: B#BrandId =:= True,
ev3: B#CatalogId =:= True,
ev4: B#SupplyChannelItemCode =:= True,
ev5: B#SupplyChannelCategoryCode =:= True,
ev6: B#Name =:= True,
ev7: B#DisplayName =:= True,
ev8: B#ItemType =:= True,
ev9: B#BasicPrice =:= True,
ev10: B#SellingPrice =:= True,
ev11: B#DiscountRate =:= True,
ev12: B#FeeRate =:= True,
...
): Item =
Item(id.get, brandId.get, catalogId.get, supplyChannelId, supplyChannelItemCode.get,
supplyChannelCategoryCode.get, name.get, displayName.get, itemType.get, basicPrice.get,
sellingPrice.get, discountRate.get, feeRate.get, periodType.get, validPeriod.get,
pinIssueType.get, pinIssueCsInfo, isCancelable.get, imageUrl, status.get, displayYn.get,
distType, detailInfo, noticeInfo, couponExpiredAt, releasedAt.get, expiredAt.get)
}
37
BuildState .
class Builder[B <: BuildState] { self =>
private var name: Option[String] = None
private var detailInfo: Option[String] = None // optional
def withName(name: String) = { //
self.name = Some(name)
newBuilder[B {type Name = True}]
}
def withDetailInfo(detailInfo: String) = { //
self.detailInfo = Some(detailInfo)
newBuilder[B]
}
}
38
39
object Builder {
def apply() = new Builder[BuildState {}]
}
//
Builder().build // doesn't compile, .
Builder()
.withBasicPrice(5000).build // doesn't compile, .
40
// . withXXX . .
val item = Builder()
.withId(10)
.withBrandId(1000)
.withCatalogId(2000)
.withSupplyChannelItemCode("I123")
.withSupplyChannelCategoryCode("C123")
.withName("( ) ")
.withDisplayName(" ")
.withItemType(101)
.withBasicPrice(5000)
.withSellingPrice(3000)
.withDiscountRate(40)
.withFeeRate(5)
.withPeriodType(2)
.withValidPeriod(365)
.withPinIssueType(3)
.withIsCancelable(true)
.withStatus(201)
.withDisplayYn(true)
.withReleasedAt(now)
.withExpiredAt(Instant.MAX)
.build
41
// named argument ... ? ?
val item = Item(
id = 10, brandId = 1000, catalogId = 2000,
supplyChannelId = None, supplyChannelItemCode = "I123",
supplyChannelCategoryCode = "C123", name = "( ) ",
displayName = " ", itemType = 101,
basicPrice = 5000, sellingPrice = 3000,
discountRate = 40, feeRate = 5,
periodType = 2, validPeriod = 365,
pinIssueType = 3, pinIssueCsInfo = None,
isCancelable = true, imageUrl = None,
status = 201, displayYn = true,
distType = None, detailInfo = None,
noticeInfo = None, couponExpiredAt = None,
releasedAt = now, expiredAt = Instant.MAX
)
42
?
43
?
?
44
Type class Pa!ern
sum .
case class Point(x: Int, y: Int) // 2
sum(List(1, 2, 3)) => 6
sum(List(Point(1,10), Point(5, 5)) => Point(6, 15)
45
trait Adder[A] {
def zero: A
def add(x: A, y: A): A
}
46
trait Adder[A] {
def zero: A
def add(x: A, y: A): A
}
def sum[A](xs: List[A])(adder: Adder[A]): A =
xs.foldLeft(adder.zero)(adder.add)
47
val intAdder = new Adder[Int] {
def zero: Int = 0
def add(x: Int, y: Int) = x + y
}
val pointAdder = new Adder[Point] {
def zero = Point(0, 0)
def add(a: Point, b: Point) = Point(a.x + b.x, a.y + b.y)
}
48
.
sum(List(1, 2, 3))(intAdder) // 7
sum(List(Point(1, 10), Point(5, 5)))(pointAdder) // Point(6, 15)
49
. .
sum(List(1, 2, 3))(intAdder) // <= .
sum(List(Point(1, 10), Point(5, 5)))(pointAdder) // <=
50
Type class pa!ern ?
def sum[A](xs: List[A])(implicit adder: Adder[A]): A
Ad-hoc polymorphism - type parameter
implicit instance polymorphism
DI - compiler dependency injection
Spring, Guice runtime DI
51
implicit
!
// Adder implicit
def sum[A](xs: List[A])(implicit adder: Adder[A]): A =
xs.foldLeft(adder.zero)(adder.add)
// type class instance implicit
implicit val intAdder = new Adder[Int] {
def zero = 0
def add(x: Int, y: Int) = x + y
}
implicit val pointAdder = new Adder[Point] {
def zero = Point(0, 0)
def add(a: Point, b: Point) = Point(a.x + b.x, a.y + b.y)
}
52
sum(List(1, 2, 3))(intAdder)
sum(List(Point(1, 10), Point(5, 5)))(pointAdder)
53
sum(List(1, 2, 3))
sum(List(Point(1, 10), Point(5, 5)))
54
sum(List(1, 2, 3))
sum(List(Point(1, 10), Point(5, 5)))
// String compile .
sum(List("Hello", "World")) // doesn't compile
//
!
55
String ?
// string instance .
implicit val stringAdder = new Adder[String] {
def zero = ""
def add(x: String, y: String) = x + y
}
sum(List("Hello", "World")) // => "HelloWorld"
56
. .
...
.
.
. .
57
58
Literal type - 42.type
?
59
Literal type - 42.type
? .
!
val t: 42 = 42
val x: "Jedi" = "Jedi"
60
Literal type - 42.type
? .
!
val t: 42 = 42
val x: "Jedi" = "Jedi"
?
61
Literal type - 42.type
? .
!
val t: 42 = 42
val x: "Jedi" = "Jedi"
? .
!
def f(t: Double): t.type = t
val a: 1.2 = f(1.2)
62
Literal type in scala
Scala http://docs.scala-lang.org/sips/pending/42.type.html
Typelevel Scala https://typelevel.org/scala/
Dotty http://dotty.epfl.ch/docs/reference/singleton-types.html
.
Literal singleton type library
.
63
true.type if condition
trait Cond[T] { type V ; val value: V }
implicit val condTrue = new Cond[true] { type V = String ; val value = "foo" }
implicit val condFalse = new Cond[false] { type V = Int ; val value = 23 }
def cond[T](implicit cond: Cond[T]): cond.V = cond.value
// true is type!
!
cond[true] // "foo"
// flase is type!
"
cond[false] // 23
64
Path Dependent type -
class A {
class B
var b: Option[B] = None
}
65
Path Dependent type -
class A {
class B
var b: Option[B] = None
}
val a1: A = new A
val a2: A = new A
val b1: a1.B = new a1.B // a1.B .
val b2: a2.B = new a2.B // a1.B a2.B .
b1 === b2 //
66
Path Dependent type -
val b1: a1.B = new a1.B // a1.B .
val b2: a2.B = new a2.B // a1.B a2.B .
a1.b = Some(b1)
a2.b = Some(b1) // does not compile
Dependent Type Programming
67
Map - .
val strIntMap: Map[String, Int] = Map("width" -> 120)
68
Map - .
val strIntMap: Map[String, Int] = Map("width" -> 120)
Sting Map Value ?
69
Map - .
val strIntMap: Map[String, Int] = Map("width" -> 120)
Sting Map Value ? Any
val strAnyMap: Map[String, Any] = strIntMap + ("sort" -> "time")
70
Map - .
val strIntMap: Map[String, Int] = Map("width" -> 120)
Sting Map Value ? Any
val strAnyMap: Map[String, Any] = strIntMap + ("sort" -> "time")
// ?
val width: Option[Any] = map2.get("width")
val sort: Option[Any] = map2.get("sort")
71
HMap - Heterogenous Map1
Dependent type !
1 
Dotty : https://www.slideshare.net/Odersky/from-dot-to-dotty
72
HMap - Heterogenous Map1
Dependent type !
trait Key { type Value }
trait HMap {
def get(key: Key): Option[key.Value] // key Value, dependent type!
def add(key: Key)(value: key.Value): HMap
}
1 
Dotty : https://www.slideshare.net/Odersky/from-dot-to-dotty
73
val sort = new Key { type Value = String }
val width = new Key { type Value = Int }
74
val sort = new Key { type Value = String }
val width = new Key { type Value = Int }
Key Value
val hmap: HMap = HMap.empty
.add(width)(120)
.add(sort)("time")
.add(width)(true) // doesn't compile, width Int Value .
75
val sort = new Key { type Value = String }
val width = new Key { type Value = Int }
Key Value
val hmap: HMap = HMap.empty
.add(width)(120)
.add(sort)("time")
.add(width)(true) // doesn't compile, width Int Value .
Value .
val optionInt: Option[Int] = hmap.get(width)
val optionString: Option[String] = hmap.get(sort)
76
HMap ?
Martin Ordersky .
77
HMap ! 9
trait HMap { self =>
val underlying: Map[Any, Any]
def get(key: Key): Option[key.Value] =
underlying.get(key).map(_.asInstanceOf[key.Value])
def add(key: Key)(value: key.Value): HMap =
new HMap {
val underlying = self.underlying + (key -> value)
}
}
78
Believing that: Life is Study!4
4 
https://twitter.com/ktosopl
79
NilOnce your code compiles it usually works.
80
Back up Slide
?
81
Back up Slide
?
?
82
Back up Slide
?
?
?
83
Back up Slide
?
?
?
84
Scala in Real World.
Parallel Programming in Micro Service Architecture.5
5 
http://tech.kakao.com/2017/09/02/parallel-programming-and-applicative-in-scala/
85
86
.
SELECT *
FROM
items, catalogs, wishes, categories, details, certificiations
WHERE
items.id = ?
AND items.id = catalogs.itemId
AND items.id = wishes.itemId
...
87
.
val product: Future[ProductDto] = for {
item <- itemReposotory.findByid(itemId) //
catalog <- catalogRepository.findById(item.catalogId) //
brand <- brandRepository.findById(item.brandId) //
wish <- itemWishCountRepository.findByItemId(item.id) //
category <- categoryRepository.findOneByBrandId(item.brandId) //
detail <- itemDetailRepository.findByItemId(item.id) //
cert <- itemCertificationRepository.findByItemId(item.id) //
} yield ProductFactory.of(item, brand, catalog, wish, category, detail, cert)
88
?
89
.
90
!!!
!
91
?
Applicative .
92
Applicative?
93
Applicative -
94
Applicative -
trait Applicative[F[_]] {
def pure[A](a: A): F[A]
def ap[A, B](fa: F[A])(ff: F[A => B]): F[B]
}
95
Applicative - ap
import scala.concurrent.Future
val futureAp = new Applicative[Future] {
def pure[A](a: A) = Future.successful(a)
def ap[A, B](fa: Future[A])(ff: Future[A => B]): Future[B] =
ff.zip(fa).map { case (f, a) => f(a)}
}
96
Applicative can be parallel
itemRepository.findById(itemId).flatMap { item =>
(
catalogRepository.findById(item.catalogId) |@|
brandRepository.findById(item.brandId) |@|
itemWishCountRepository.findByItemId(item.id) |@|
categoryRepository.findOneByBrandId(item.brandId) |@|
itemDetailRepository.findByItemId(item.id) |@|
itemCertificationRepository.findByItemId(item.id)
).map { case (catalog, brand, wish, category, detail, cert) =>
List(brand, catalog, wish, category, detail, cert)
}
}
97
98
99
Backgroud - Phantom type
!
Phantom( ) type Compile time
Runtime
trait Phantom //
type MyInt = Int with Phantom // Phantom type mixin
100
Int has no meaning
A Int can contains anything
Int 42 9496 7296 .
101
def findItemsBy(brandId: Int, itemId: Int,
status: Int, page: Int, size: Int = 20) =
items.filter(_.brandId == brandId).drop(page * size).take(size)
// ItemService.scala
// .
findItemsByBrandId(brandId, itemId, status, page, size)
102
?
// catalogId , Id
def findItemsBy(brandId: Int, itemId: Int,catalogId: Int,
status: Int, page: Int, size: Int = 20) =
items.filter(_.brandId == brandId).drop(page * size).take(size)
// BrandService.scala
// API .
findItemsByBrandId(brandId, itemId, catalogId, status, page, size)
103
?
// catalogId , Id
def findItemsBy(brandId: Int, itemId: Int,catalogId: Int,
status: Int, page: Int, size: Int = 20) =
items.filter(_.brandId == brandId).drop(page * size).take(size)
// BrandService.scala
// API .
findItemsByBrandId(brandId, itemId, catalogId, status, page, size)
// ItemService.scala
// ?
findItemsByBrandId(brandId, itemId, status, page, size)
104
.
?
105
User defined type
Compiler .
case class ItemId(value: Int) extends AnyVal
case class CatalogId(value: Int) extends AnyVal
case class BrandId(value: Int) extends AnyVal
case class Status(value: Int) extends AnyVal
case class Page(value: Int) extends AnyVal
case class Size(value: Int) extends AnyVal
106
API
def findItemsByBrandId(
brandId: BrandId, itemId: ItemId, catalogId: CatalogId,
status: Status, page: Page, size: Size = Size(20)
): List[Item] =
items.filter(_.brandId == brandId.value)
.drop(page.value * size.value)
.take(size.value)
findItemsByBrandId(brandId, itemId, status, page, size) // doesn't compile,
107
? .
!
// before
def findItemsBy(
brandId: Int, itemId: Int,catalogId: Int,
status: Int, page: Int, size: Int = 20): List[Item] =
items
.filter(_.brandId == brandId)
.drop(page * size)
.take(size)
// after
def findItemsByBrandId(
brandId: BrandId, itemId: ItemId, catalogId: CatalogId,
status: Status, page: Page, size: Size = Size(20)): List[Item] =
items.filter(_.brandId == brandId.value)
.drop(page.value * size.value) //
.take(size.value) //
?
!
?
"
108
Page type
Int Int .
case class Page(value: Int) extends AnyVal
Page Int ? Page extends Int
Page Int .
val page: Int = Page(1) // .
109
shapeless .
import shapeless._
val shapely =
"Dependent type programming"
:: "Generic type programming" :: HNil
https://github.com/milessabin/shapeless
.
110
Tagged type
!
Phantom( ) type Compile time ( )
TypeTag @@ Phantom type
import shapeless.tag.@@
type Page = Int @@ PageTag // Page Int PageTag
val page: Page = tag[PageTag][Int](1) // ,
111
Tagged type
!
Phantom( ) type Compile time
TypeTag @@ Phantom type
import shapeless.tag.@@
type Page = Int @@ PageTag // Page Int PageTag
val page: Page = tag[PageTag][Int](1) // ,
// PageTag Int
page.getClass // => class java.lang.Integer
// Int .
val int : Int = page
112
Phantom type
— .
— (final) .
— API .
— .
113
import shapeless.tag.@@
trait BrandIdTag; trait ItemIdTag
trait BrandTag; trait ItemTag
trait PageTag; trait SizeTag
object Pagable {
type Page = Int @@ PageTag // Page Int
type Size = Int @@ SizeTag // Size Int
}
def findItemsByBrandId(brandId: Int, page: Page, size: Size): List[Item] =
items.filter(_.brandId == brandId)
.drop(page * size) // Page => Int !
.take(size) //
val page: Page = tag[PageTag][Int](1)
val size: Size = tag[SizeTag][Int](20)
findItemsByBrandId(1, page, size) // works
// page size .
findItemsByBrandId(1, size, page) // doesn't compile
114

More Related Content

PDF
BabelJS - James Kyle at Modern Web UI
PDF
From android/java to swift (3)
PPT
Understanding Reflection
PPTX
INHERITANCE IN C++ +2 COMPUTER SCIENCE CBSE AND STATE SYLLABUS
PDF
Advanced CPP Lecture 1- Summer School 2014 - ACA CSE IITK
PDF
ECMA 入门
PPTX
Introduction to JQuery
PPT
Java script -23jan2015
BabelJS - James Kyle at Modern Web UI
From android/java to swift (3)
Understanding Reflection
INHERITANCE IN C++ +2 COMPUTER SCIENCE CBSE AND STATE SYLLABUS
Advanced CPP Lecture 1- Summer School 2014 - ACA CSE IITK
ECMA 入门
Introduction to JQuery
Java script -23jan2015

What's hot (10)

PPTX
Swift as an OOP Language
PDF
Mirror, mirror on the wall - Building a new PHP reflection library (Nomad PHP...
PPTX
Java script basics
PPTX
ECMA5 approach to building JavaScript frameworks with Anzor Bashkhaz
PDF
Practical Object-Oriented Back-in-Time Debugging
PDF
JavaScript symbols
PDF
Leveraging Symfony2 Forms
PDF
Building Reusable Custom Elements With Angular
PDF
Sketchmine: Design Systems Tooling
PDF
Everything you always wanted to know about forms* *but were afraid to ask
Swift as an OOP Language
Mirror, mirror on the wall - Building a new PHP reflection library (Nomad PHP...
Java script basics
ECMA5 approach to building JavaScript frameworks with Anzor Bashkhaz
Practical Object-Oriented Back-in-Time Debugging
JavaScript symbols
Leveraging Symfony2 Forms
Building Reusable Custom Elements With Angular
Sketchmine: Design Systems Tooling
Everything you always wanted to know about forms* *but were afraid to ask
Ad

Similar to Type level programming in Scala (20)

PPT
11MappingDesigntoCode.ppt Software engineering
PPT
11MappingDesigntoCode.ppt ooad software software
PDF
ADG Poznań - Kotlin for Android developers
PDF
Dart for Java Developers
DOC
Design Patterns
PDF
2 kotlin vs. java: what java has that kotlin does not
PPT
CONSTRUCTORS IN C++ +2 COMPUTER SCIENCE
PDF
Lift off with Groovy 2 at JavaOne 2013
PDF
A quick and fast intro to Kotlin
PDF
Kotlin for Android devs
PPTX
14. Java defining classes
PPTX
Applying Compiler Techniques to Iterate At Blazing Speed
PPTX
Intro to object oriented programming
PDF
Writing browser extensions_in_kotlin
PPTX
CSharp presentation and software developement
PPT
14. Defining Classes
PPTX
PPTX
R. herves. clean code (theme)2
PPTX
Oop2010 Scala Presentation Stal
PDF
XAML/C# to HTML/JS
11MappingDesigntoCode.ppt Software engineering
11MappingDesigntoCode.ppt ooad software software
ADG Poznań - Kotlin for Android developers
Dart for Java Developers
Design Patterns
2 kotlin vs. java: what java has that kotlin does not
CONSTRUCTORS IN C++ +2 COMPUTER SCIENCE
Lift off with Groovy 2 at JavaOne 2013
A quick and fast intro to Kotlin
Kotlin for Android devs
14. Java defining classes
Applying Compiler Techniques to Iterate At Blazing Speed
Intro to object oriented programming
Writing browser extensions_in_kotlin
CSharp presentation and software developement
14. Defining Classes
R. herves. clean code (theme)2
Oop2010 Scala Presentation Stal
XAML/C# to HTML/JS
Ad

Recently uploaded (20)

PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
PPTX
Introduction to Artificial Intelligence
PPTX
Reimagine Home Health with the Power of Agentic AI​
PDF
Softaken Excel to vCard Converter Software.pdf
PPTX
Transform Your Business with a Software ERP System
PDF
wealthsignaloriginal-com-DS-text-... (1).pdf
PDF
Digital Strategies for Manufacturing Companies
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PDF
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
PDF
top salesforce developer skills in 2025.pdf
PPTX
L1 - Introduction to python Backend.pptx
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PDF
medical staffing services at VALiNTRY
PDF
Navsoft: AI-Powered Business Solutions & Custom Software Development
PDF
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
PPTX
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
PDF
Adobe Illustrator 28.6 Crack My Vision of Vector Design
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
Introduction to Artificial Intelligence
Reimagine Home Health with the Power of Agentic AI​
Softaken Excel to vCard Converter Software.pdf
Transform Your Business with a Software ERP System
wealthsignaloriginal-com-DS-text-... (1).pdf
Digital Strategies for Manufacturing Companies
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
top salesforce developer skills in 2025.pdf
L1 - Introduction to python Backend.pptx
How to Choose the Right IT Partner for Your Business in Malaysia
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
medical staffing services at VALiNTRY
Navsoft: AI-Powered Business Solutions & Custom Software Development
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
Adobe Illustrator 28.6 Crack My Vision of Vector Design

Type level programming in Scala

  • 1. Type Level Programming in scala @liam.m 1
  • 2. Requirements — Scala? . — ! — ! — ! — ! 2
  • 4. ? 4
  • 5. Once your code compiles it usually works.2 . 2  https://wiki.haskell.org/Why_Haskell_just_works 5
  • 6. TDD TDD - Type driven development3 3  https://www.manning.com/books/type-driven-development-with-idris 6
  • 7. 1. Type safe equality - === 2. Builder pattern using Phantom type 3. Type class pattern - implicit 4. Literal type - 42.type 5. Dependent type - a.B Type . 7
  • 8. Equality? val items = List( Item(1, Some("ON_SALE")), Item(2, Some("SOLDOUT")), Item(3, Some("ON_SALE")), Item(4, None)) items.filter(item => item.status == "ON_SALE") ? 8
  • 9. Equality? val items = List( Item(1, Some("ON_SALE")), Item(2, Some("SOLDOUT")), Item(3, Some("ON_SALE")), Item(4, None)) items.filter(item => item.status == "ON_SALE") Nil . item.status: Option[String] != "ON_SALE": String 9
  • 10. . ! test("item status filter") { val expected = List( Item(1, Some("ON_SALE")), Item(3, Some("ON_SALE")) ) assert(items == expected) // ! } 10
  • 11. . ! test("item status filter") { val expected = List( Item(1, Some("ON_SALE")), Item(3, Some("ON_SALE")) ) assert(items == expected) // ! } ? 11
  • 12. . ! test("item status filter") { val expected = List( Item(1, Some("ON_SALE")), Item(3, Some("ON_SALE")) ) assert(items == expected) // ! } ? Once your code compiles it usually works. 12
  • 13. Type safe equality implicit class StrictEq[A](a: A) { def ===(b: A) = a == b } 13
  • 14. Type safe equality implicit class StrictEq[A](a: A) { def ===(b: A) = a == b } implicit class . A === 14
  • 15. COMPILE TIME . val x = Some("ON_SALE") val y = Some("SOLDOUT") val z = "ON_SALE" x === y // false x === z // doesn't compile, . ⛑ 15
  • 17. scalacOptions += "-Xfatal-warnings" case class UserId(n: Int) val john = UserId(7) john == 7 // <console>:11: warning: comparing values of // types UserId and Int using `==' // will always yield false john == 7 warning -> error 17
  • 18. 18
  • 19. 19
  • 20. Builder pa!ern "The builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters." Joshua Bloch, Effective Java ! 20
  • 21. Builder pa!ern "The builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters." Joshua Bloch, Effective Java ! 21
  • 22. // , . public class Item { private Integer id; private Integer brandId; private Integer catalogId; private Integer supplyChannelId; // optional private String supplyChannelItemCode; private String supplyChannelCategoryCode; private String name; private String displayName; private Integer itemType; private Integer basicPrice; private Integer sellingPrice; private Integer discountRate; private Integer feeRate; private Byte periodType; private Integer validPeriod; private Byte pinIssueType; private String pinIssueCsInfo; // optional private Boolean isCancelable; private String imageUrl; // optional private Integer status; private Boolean displayYn; private Short distType; // optional private String detailInfo; // optional private String noticeInfo; // optional private Instant couponExpiredAt; // optional private Instant releasedAt; private Instant expiredAt; ... // 51 } ? 22
  • 23. - Joshua Bloch public class Item { public static class Builder { // Builder public Builder(Integer id, Integer brandId, Integer catalogId, String supplyChannelItemCode, String supplyChannelCategoryCode, String name, String displayName, Integer itemType, Integer basicPrice, Integer sellingPrice, Integer discountRate, Integer feeRate, Byte periodType, Integer validPeriod, String pinIssueCsInfo, Byte pinIssueType, Boolean isCancelable, Integer status, Boolean displayYn, Instant releasedAt, Instant expiredAt) { this.id = id; this.brandId = brandId; this.catalogId = catalogId; this.supplyChannelItemCode = supplyChannelItemCode; this.supplyChannelCategoryCode = supplyChannelCategoryCode; this.name = name; this.displayName = displayName; this.itemType = itemType; this.basicPrice = basicPrice; this.sellingPrice = sellingPrice; this.discountRate = discountRate; this.feeRate = feeRate; this.periodType = periodType; this.validPeriod = validPeriod; this.pinIssueType = pinIssueType; this.pinIssueCsInfo = pinIssueCsInfo; this.isCancelable = isCancelable; this.status = status; this.displayYn = displayYn; this.releasedAt = releasedAt; this.expiredAt = expiredAt; } // public Builder withSupplyChannelId(Integer val) { this.supplyChannelId = val; return this; } public Builder withPinIssueCsInfo(String val) { this.pinIssueCsInfo = val; return this; } ... } } 23
  • 24. Builder builder . Item item = new Item.Builder( 10, 1000, 2000, "I123", "C123", "( ) ", " ", 101, 5000, 3000, 40, 5, (byte)2, 365, "CS", (byte) 3, true, 201, true, Instant.now(), Instant.MAX ).withNoticeInfo(" ").withImageUrl("http://builder.jpg") .build(); 24
  • 25. : Builder builder — Builder withXXX 25
  • 26. /* Item.scala */ case class Item( id: Int, brandId: Int, catalogId: Int, supplyChannelId: Option[Int], // supplyChannelItemCode: String, // . supplyChannelCategoryCode: String, name: String, displayName: String, itemType: Int, basicPrice: Int, sellingPrice: Int, discountRate: Int, feeRate: Int, periodType: Byte, validPeriod: Int, pinIssueType: Byte, pinIssueCsInfo: Option[String], isCancelable: Boolean, imageUrl: Option[String], status: Int, displayYn: Boolean, distType: Option[Short], detailInfo: Option[String], noticeInfo: Option[String], couponExpiredAt: Option[Instant], releasedAt: Instant, expiredAt: Instant ) 26
  • 29. type . Why? Compiler type type 29
  • 30. type . Why? Compiler type type // . sealed trait BuildState { type Id <: Bool // True type Name <: Bool // True } 30
  • 31. sealed trait Bool val True = new Bool {} val False = new Bool {} val hasOption: Bool = True 31
  • 32. sealed trait Bool sealed trait True extends Bool sealed trait False extends Bool type HasOption = True 32
  • 33. — ADT Values : val → trait val True = new Bool {} ➜ trait True extends Bool 33
  • 34. — ADT Values : val → trait val True = new Bool {} ➜ trait True extends Bool — members : val → type X val hasOption: Bool = True ➜ type HasOption = True 34
  • 35. sealed trait Bool // val True = new Bool {} val False = new Bool {} val hasOption: Bool = True // sealed trait True extends Bool sealed trait False extends Bool type HasOption = True 35
  • 36. Name, DisplayName True class Builder[B <: BuildState] { self => private var name: Option[String] = None private var displayName: Option[String] = None def newBuilder[C <: BuildState] = this.asInstanceOf[Builder[C]] def withName(name: String) = { self.name = Some(name) newBuilder[B {type Name = True}] } def withDisplayName(displayName: String) = { self.displayName = Some(displayName) newBuilder[B {type DisplayName = True}] } } 36
  • 37. True class Builder[B <: BuildState] { self => def build(implicit ev1: B#Id =:= True, // True ev2: B#BrandId =:= True, ev3: B#CatalogId =:= True, ev4: B#SupplyChannelItemCode =:= True, ev5: B#SupplyChannelCategoryCode =:= True, ev6: B#Name =:= True, ev7: B#DisplayName =:= True, ev8: B#ItemType =:= True, ev9: B#BasicPrice =:= True, ev10: B#SellingPrice =:= True, ev11: B#DiscountRate =:= True, ev12: B#FeeRate =:= True, ... ): Item = Item(id.get, brandId.get, catalogId.get, supplyChannelId, supplyChannelItemCode.get, supplyChannelCategoryCode.get, name.get, displayName.get, itemType.get, basicPrice.get, sellingPrice.get, discountRate.get, feeRate.get, periodType.get, validPeriod.get, pinIssueType.get, pinIssueCsInfo, isCancelable.get, imageUrl, status.get, displayYn.get, distType, detailInfo, noticeInfo, couponExpiredAt, releasedAt.get, expiredAt.get) } 37
  • 38. BuildState . class Builder[B <: BuildState] { self => private var name: Option[String] = None private var detailInfo: Option[String] = None // optional def withName(name: String) = { // self.name = Some(name) newBuilder[B {type Name = True}] } def withDetailInfo(detailInfo: String) = { // self.detailInfo = Some(detailInfo) newBuilder[B] } } 38
  • 39. 39
  • 40. object Builder { def apply() = new Builder[BuildState {}] } // Builder().build // doesn't compile, . Builder() .withBasicPrice(5000).build // doesn't compile, . 40
  • 41. // . withXXX . . val item = Builder() .withId(10) .withBrandId(1000) .withCatalogId(2000) .withSupplyChannelItemCode("I123") .withSupplyChannelCategoryCode("C123") .withName("( ) ") .withDisplayName(" ") .withItemType(101) .withBasicPrice(5000) .withSellingPrice(3000) .withDiscountRate(40) .withFeeRate(5) .withPeriodType(2) .withValidPeriod(365) .withPinIssueType(3) .withIsCancelable(true) .withStatus(201) .withDisplayYn(true) .withReleasedAt(now) .withExpiredAt(Instant.MAX) .build 41
  • 42. // named argument ... ? ? val item = Item( id = 10, brandId = 1000, catalogId = 2000, supplyChannelId = None, supplyChannelItemCode = "I123", supplyChannelCategoryCode = "C123", name = "( ) ", displayName = " ", itemType = 101, basicPrice = 5000, sellingPrice = 3000, discountRate = 40, feeRate = 5, periodType = 2, validPeriod = 365, pinIssueType = 3, pinIssueCsInfo = None, isCancelable = true, imageUrl = None, status = 201, displayYn = true, distType = None, detailInfo = None, noticeInfo = None, couponExpiredAt = None, releasedAt = now, expiredAt = Instant.MAX ) 42
  • 43. ? 43
  • 45. Type class Pa!ern sum . case class Point(x: Int, y: Int) // 2 sum(List(1, 2, 3)) => 6 sum(List(Point(1,10), Point(5, 5)) => Point(6, 15) 45
  • 46. trait Adder[A] { def zero: A def add(x: A, y: A): A } 46
  • 47. trait Adder[A] { def zero: A def add(x: A, y: A): A } def sum[A](xs: List[A])(adder: Adder[A]): A = xs.foldLeft(adder.zero)(adder.add) 47
  • 48. val intAdder = new Adder[Int] { def zero: Int = 0 def add(x: Int, y: Int) = x + y } val pointAdder = new Adder[Point] { def zero = Point(0, 0) def add(a: Point, b: Point) = Point(a.x + b.x, a.y + b.y) } 48
  • 49. . sum(List(1, 2, 3))(intAdder) // 7 sum(List(Point(1, 10), Point(5, 5)))(pointAdder) // Point(6, 15) 49
  • 50. . . sum(List(1, 2, 3))(intAdder) // <= . sum(List(Point(1, 10), Point(5, 5)))(pointAdder) // <= 50
  • 51. Type class pa!ern ? def sum[A](xs: List[A])(implicit adder: Adder[A]): A Ad-hoc polymorphism - type parameter implicit instance polymorphism DI - compiler dependency injection Spring, Guice runtime DI 51
  • 52. implicit ! // Adder implicit def sum[A](xs: List[A])(implicit adder: Adder[A]): A = xs.foldLeft(adder.zero)(adder.add) // type class instance implicit implicit val intAdder = new Adder[Int] { def zero = 0 def add(x: Int, y: Int) = x + y } implicit val pointAdder = new Adder[Point] { def zero = Point(0, 0) def add(a: Point, b: Point) = Point(a.x + b.x, a.y + b.y) } 52
  • 53. sum(List(1, 2, 3))(intAdder) sum(List(Point(1, 10), Point(5, 5)))(pointAdder) 53
  • 54. sum(List(1, 2, 3)) sum(List(Point(1, 10), Point(5, 5))) 54
  • 55. sum(List(1, 2, 3)) sum(List(Point(1, 10), Point(5, 5))) // String compile . sum(List("Hello", "World")) // doesn't compile // ! 55
  • 56. String ? // string instance . implicit val stringAdder = new Adder[String] { def zero = "" def add(x: String, y: String) = x + y } sum(List("Hello", "World")) // => "HelloWorld" 56
  • 58. 58
  • 59. Literal type - 42.type ? 59
  • 60. Literal type - 42.type ? . ! val t: 42 = 42 val x: "Jedi" = "Jedi" 60
  • 61. Literal type - 42.type ? . ! val t: 42 = 42 val x: "Jedi" = "Jedi" ? 61
  • 62. Literal type - 42.type ? . ! val t: 42 = 42 val x: "Jedi" = "Jedi" ? . ! def f(t: Double): t.type = t val a: 1.2 = f(1.2) 62
  • 63. Literal type in scala Scala http://docs.scala-lang.org/sips/pending/42.type.html Typelevel Scala https://typelevel.org/scala/ Dotty http://dotty.epfl.ch/docs/reference/singleton-types.html . Literal singleton type library . 63
  • 64. true.type if condition trait Cond[T] { type V ; val value: V } implicit val condTrue = new Cond[true] { type V = String ; val value = "foo" } implicit val condFalse = new Cond[false] { type V = Int ; val value = 23 } def cond[T](implicit cond: Cond[T]): cond.V = cond.value // true is type! ! cond[true] // "foo" // flase is type! " cond[false] // 23 64
  • 65. Path Dependent type - class A { class B var b: Option[B] = None } 65
  • 66. Path Dependent type - class A { class B var b: Option[B] = None } val a1: A = new A val a2: A = new A val b1: a1.B = new a1.B // a1.B . val b2: a2.B = new a2.B // a1.B a2.B . b1 === b2 // 66
  • 67. Path Dependent type - val b1: a1.B = new a1.B // a1.B . val b2: a2.B = new a2.B // a1.B a2.B . a1.b = Some(b1) a2.b = Some(b1) // does not compile Dependent Type Programming 67
  • 68. Map - . val strIntMap: Map[String, Int] = Map("width" -> 120) 68
  • 69. Map - . val strIntMap: Map[String, Int] = Map("width" -> 120) Sting Map Value ? 69
  • 70. Map - . val strIntMap: Map[String, Int] = Map("width" -> 120) Sting Map Value ? Any val strAnyMap: Map[String, Any] = strIntMap + ("sort" -> "time") 70
  • 71. Map - . val strIntMap: Map[String, Int] = Map("width" -> 120) Sting Map Value ? Any val strAnyMap: Map[String, Any] = strIntMap + ("sort" -> "time") // ? val width: Option[Any] = map2.get("width") val sort: Option[Any] = map2.get("sort") 71
  • 72. HMap - Heterogenous Map1 Dependent type ! 1  Dotty : https://www.slideshare.net/Odersky/from-dot-to-dotty 72
  • 73. HMap - Heterogenous Map1 Dependent type ! trait Key { type Value } trait HMap { def get(key: Key): Option[key.Value] // key Value, dependent type! def add(key: Key)(value: key.Value): HMap } 1  Dotty : https://www.slideshare.net/Odersky/from-dot-to-dotty 73
  • 74. val sort = new Key { type Value = String } val width = new Key { type Value = Int } 74
  • 75. val sort = new Key { type Value = String } val width = new Key { type Value = Int } Key Value val hmap: HMap = HMap.empty .add(width)(120) .add(sort)("time") .add(width)(true) // doesn't compile, width Int Value . 75
  • 76. val sort = new Key { type Value = String } val width = new Key { type Value = Int } Key Value val hmap: HMap = HMap.empty .add(width)(120) .add(sort)("time") .add(width)(true) // doesn't compile, width Int Value . Value . val optionInt: Option[Int] = hmap.get(width) val optionString: Option[String] = hmap.get(sort) 76
  • 78. HMap ! 9 trait HMap { self => val underlying: Map[Any, Any] def get(key: Key): Option[key.Value] = underlying.get(key).map(_.asInstanceOf[key.Value]) def add(key: Key)(value: key.Value): HMap = new HMap { val underlying = self.underlying + (key -> value) } } 78
  • 79. Believing that: Life is Study!4 4  https://twitter.com/ktosopl 79
  • 80. NilOnce your code compiles it usually works. 80
  • 85. Scala in Real World. Parallel Programming in Micro Service Architecture.5 5  http://tech.kakao.com/2017/09/02/parallel-programming-and-applicative-in-scala/ 85
  • 86. 86
  • 87. . SELECT * FROM items, catalogs, wishes, categories, details, certificiations WHERE items.id = ? AND items.id = catalogs.itemId AND items.id = wishes.itemId ... 87
  • 88. . val product: Future[ProductDto] = for { item <- itemReposotory.findByid(itemId) // catalog <- catalogRepository.findById(item.catalogId) // brand <- brandRepository.findById(item.brandId) // wish <- itemWishCountRepository.findByItemId(item.id) // category <- categoryRepository.findOneByBrandId(item.brandId) // detail <- itemDetailRepository.findByItemId(item.id) // cert <- itemCertificationRepository.findByItemId(item.id) // } yield ProductFactory.of(item, brand, catalog, wish, category, detail, cert) 88
  • 89. ? 89
  • 90. . 90
  • 95. Applicative - trait Applicative[F[_]] { def pure[A](a: A): F[A] def ap[A, B](fa: F[A])(ff: F[A => B]): F[B] } 95
  • 96. Applicative - ap import scala.concurrent.Future val futureAp = new Applicative[Future] { def pure[A](a: A) = Future.successful(a) def ap[A, B](fa: Future[A])(ff: Future[A => B]): Future[B] = ff.zip(fa).map { case (f, a) => f(a)} } 96
  • 97. Applicative can be parallel itemRepository.findById(itemId).flatMap { item => ( catalogRepository.findById(item.catalogId) |@| brandRepository.findById(item.brandId) |@| itemWishCountRepository.findByItemId(item.id) |@| categoryRepository.findOneByBrandId(item.brandId) |@| itemDetailRepository.findByItemId(item.id) |@| itemCertificationRepository.findByItemId(item.id) ).map { case (catalog, brand, wish, category, detail, cert) => List(brand, catalog, wish, category, detail, cert) } } 97
  • 98. 98
  • 99. 99
  • 100. Backgroud - Phantom type ! Phantom( ) type Compile time Runtime trait Phantom // type MyInt = Int with Phantom // Phantom type mixin 100
  • 101. Int has no meaning A Int can contains anything Int 42 9496 7296 . 101
  • 102. def findItemsBy(brandId: Int, itemId: Int, status: Int, page: Int, size: Int = 20) = items.filter(_.brandId == brandId).drop(page * size).take(size) // ItemService.scala // . findItemsByBrandId(brandId, itemId, status, page, size) 102
  • 103. ? // catalogId , Id def findItemsBy(brandId: Int, itemId: Int,catalogId: Int, status: Int, page: Int, size: Int = 20) = items.filter(_.brandId == brandId).drop(page * size).take(size) // BrandService.scala // API . findItemsByBrandId(brandId, itemId, catalogId, status, page, size) 103
  • 104. ? // catalogId , Id def findItemsBy(brandId: Int, itemId: Int,catalogId: Int, status: Int, page: Int, size: Int = 20) = items.filter(_.brandId == brandId).drop(page * size).take(size) // BrandService.scala // API . findItemsByBrandId(brandId, itemId, catalogId, status, page, size) // ItemService.scala // ? findItemsByBrandId(brandId, itemId, status, page, size) 104
  • 106. User defined type Compiler . case class ItemId(value: Int) extends AnyVal case class CatalogId(value: Int) extends AnyVal case class BrandId(value: Int) extends AnyVal case class Status(value: Int) extends AnyVal case class Page(value: Int) extends AnyVal case class Size(value: Int) extends AnyVal 106
  • 107. API def findItemsByBrandId( brandId: BrandId, itemId: ItemId, catalogId: CatalogId, status: Status, page: Page, size: Size = Size(20) ): List[Item] = items.filter(_.brandId == brandId.value) .drop(page.value * size.value) .take(size.value) findItemsByBrandId(brandId, itemId, status, page, size) // doesn't compile, 107
  • 108. ? . ! // before def findItemsBy( brandId: Int, itemId: Int,catalogId: Int, status: Int, page: Int, size: Int = 20): List[Item] = items .filter(_.brandId == brandId) .drop(page * size) .take(size) // after def findItemsByBrandId( brandId: BrandId, itemId: ItemId, catalogId: CatalogId, status: Status, page: Page, size: Size = Size(20)): List[Item] = items.filter(_.brandId == brandId.value) .drop(page.value * size.value) // .take(size.value) // ? ! ? " 108
  • 109. Page type Int Int . case class Page(value: Int) extends AnyVal Page Int ? Page extends Int Page Int . val page: Int = Page(1) // . 109
  • 110. shapeless . import shapeless._ val shapely = "Dependent type programming" :: "Generic type programming" :: HNil https://github.com/milessabin/shapeless . 110
  • 111. Tagged type ! Phantom( ) type Compile time ( ) TypeTag @@ Phantom type import shapeless.tag.@@ type Page = Int @@ PageTag // Page Int PageTag val page: Page = tag[PageTag][Int](1) // , 111
  • 112. Tagged type ! Phantom( ) type Compile time TypeTag @@ Phantom type import shapeless.tag.@@ type Page = Int @@ PageTag // Page Int PageTag val page: Page = tag[PageTag][Int](1) // , // PageTag Int page.getClass // => class java.lang.Integer // Int . val int : Int = page 112
  • 113. Phantom type — . — (final) . — API . — . 113
  • 114. import shapeless.tag.@@ trait BrandIdTag; trait ItemIdTag trait BrandTag; trait ItemTag trait PageTag; trait SizeTag object Pagable { type Page = Int @@ PageTag // Page Int type Size = Int @@ SizeTag // Size Int } def findItemsByBrandId(brandId: Int, page: Page, size: Size): List[Item] = items.filter(_.brandId == brandId) .drop(page * size) // Page => Int ! .take(size) // val page: Page = tag[PageTag][Int](1) val size: Size = tag[SizeTag][Int](20) findItemsByBrandId(1, page, size) // works // page size . findItemsByBrandId(1, size, page) // doesn't compile 114