The document discusses Kotlin DSL and how it can be used to create domain-specific languages. It provides examples of using Kotlin features like operators, lambdas, and extensions to build DSLs for validation rules, UI layouts, dependency injection, and more. Key points include how functions, operators, and properties can be overridden or extended to define new syntax and behaviors for DSLs, and how inline functions allow embedding DSL code without additional overhead.
24. Validation<User> {
"name"{
be { name.isNotBlank() } not "入力してください"
be { name.length >= 5 } not "5文字以上入力"
be { name.length <= 10 } not "10文字以下で入力"
}
"age"{
be { age >= 0 } not "入力してください"
be { age >= 20 } not "20歳以上で入力"
}
}
•
30. operator invoke + lambda
Validation<User> {
"name"{
be { name.isNotBlank() } not "入力してください"
be { name.length >= 5 } not "5文字以上入力"
be { name.length <= 10 } not "10文字以下で入力"
}
}
•
class Validation<T> {
companion object {
operator fun <T>
invoke(init: Validation<T>.() -> Unit): Validation<T> {
return Validation<T>().apply(init)
}
}
}
31. + lambda
Validation<User> {
"name"{
be { name.isNotBlank() } not "入力してください"
be { name.length >= 5 } not "5文字以上入力"
be { name.length <= 10 } not "10文字以下で入力"
}
}
•
class Validation<T> {
val validations = mutableMapOf<String, ChildValidation<T>>()
operator fun String.invoke(
init: ChildValidation<T>.() -> Unit
) {
validations.put(this, ChildValidation<T>().apply(init))
}
}
32. infix( )
Validation<User> {
"name"{
be { name.isNotBlank() } not "入力してください"
be { name.length >= 5 } not "5文字以上入力"
be { name.length <= 10 } not "10文字以下で入力"
}
}
•
class ChildValidation<T> {
fun be(f: T.() -> Boolean) = f
infix fun (T.() -> Boolean).not(message: String)
}
34. DSL
be { name.isNotBlank() } not "入力してください"
class ChildValidation<T> {
val validations = mutableListOf<Pair<T.() -> Boolean, String>>()
infix fun be(f: T.() -> Boolean) = f
infix fun (T.() -> Boolean).not(message: String) {
validations.add(Pair(this, message))
}
}
•
35. DSL
Validation<User> {
"name"{
be { name.isNotBlank() } not "入力してください"
be { name.length >= 5 } not "5文字以上入力"
be { name.length <= 10 } not "10文字以下で入力"
}
"age"{
be { age >= 0 } not "入力してください"
be { age >= 20 } not "20歳以上で入力"
}
}
•
validate
43. inline
class Sample {
fun run(f: () -> Unit) {
f()
}
inline fun inlineRun(f: () -> Unit) {
f()
}
fun main() {
run { print("run") }
inlineRun { print("inlineRun") }
}
}
44. public final class Sample {
public final void run(@NotNull Function0 f) {
Intrinsics.checkParameterIsNotNull(f, "f");
f.invoke();
}
public final void inlineRun(@NotNull Function0 f) {
Intrinsics.checkParameterIsNotNull(f, "f");
f.invoke();
}
public final void main() {
this.run((Function0)null.INSTANCE);
String var2 = "inlineRun";
System.out.print(var2);
}
}
45. Inline
public final class Sample {
public final void run(@NotNull Function0 f) {
Intrinsics.checkParameterIsNotNull(f, "f");
f.invoke();
}
public final void inlineRun(@NotNull Function0 f) {
Intrinsics.checkParameterIsNotNull(f, "f");
f.invoke();
}
public final void main() {
this.run((Function0)null.INSTANCE);
String var2 = "inlineRun";
System.out.print(var2);
}
}
46.
47. • X be Y not error_message
• not
• not
error message
• be 20
not error message “20 ”
48. Validation<User> {
"name"{
be { name.length >= 5 } not "5文字以下"
be { name.length <= 10 } not "10文字以下"
}
"age"{
be { age >= 20 } not error message "20歳以上"
}
}
•
49. fun be(f: T.() -> Boolean) = f
infix fun (T.() -> Boolean).not(???) : ???
be { age >= 20 } not error message "20歳以上"
• error message
•
50. Syntax object
object error
infix fun (T.() -> Boolean).not(syntax: error) = this
infix fun (T.() -> Boolean).message(message: String) {
validations.add(Pair(this, message))
}
be { age >= 20 } not error message "20歳以上"
• object error this
• not message
51. Syntax object
object error
infix fun (T.() -> Boolean).not(syntax: error) = this
infix fun (T.() -> Boolean).message(message: String) {
validations.add(Pair(this, message))
}
be { age >= 20 } not error message “20歳以上"
• object error this
• not message
52. IDE
be { age >= 0 } not error not error not error …
•
• not error
•
•
• not error message
53. IDE
class ErrorMessageSyntax<T>(val validation: T.() -> Boolean)
infix fun (T.() -> Boolean).not(syntax: error) =
ErrorMessageSyntax(this)
infix fun ErrorMessageSyntax<T>.message(message: String) {
validations.add(Pair(this.validation, message))
}
•
•
•
55. be { age >= 20 } not with callback "20歳以上入力"{
print("debug log")
}
class WithCallbackSyntax<T>(val validation: T.() -> Boolean)
infix fun (T.() -> Boolean).not(syntax: with) =
WithCallbackSyntax(this)
•
• error with
•
• GitHub
62. Validations {
define<User> {
"name"{
be { name.isNotBlank() } not "入力してください"
be { name.length >= 5 } not "5文字以上入力"
be { name.length <= 10 } not "10文字以下で入力"
}
"age"{
be { age >= 0 } not "入力してください"
be { age >= 20 } not "20歳以上で入力"
}
}
define<Todo> {
"body"{
be { body.isNotBlank() } not "入力してください"
}
}
}
66. Validations {
define<User> {
"name"{
be { name.length >= 5 } not "5文字以上入力"
be { name.length <= 10 } not "10文字以下で入力"
}
"age"{
be { age >= 0 } not "入力してください"
be { age >= 20 } not "20歳以上で入力"
}
}
define<Todo> {
"body"{
be { body.isNotBlank() } not "入力してください"
}
}
}
//{name=[5文字以上入力], age=[20歳以上で入力]}
Validations.validate(User("k",1).validate())
//{body=[入力してください]}
Validations.validate(Todo("").validate())
70. Validations {
define<User> {
"name"{
be { name.length >= 5 } not "5文字以上入力"
be { name.length <= 10 } not "10文字以下で入力"
}
"age"{
be { age >= 0 } not "入力してください"
be { age >= 20 } not "20歳以上で入力"
}
}
define<Todo> {
"body"{
be { body.isNotBlank() } not "入力してください"
}
}
}
//{name=[5文字以上入力], age=[20歳以上で入力]}
User("k",1).validate()
//{body=[入力してください]}
Todo("").validate()
73. interface Validable
inline fun <reified T : Validable> T.validate()
= Validations.validate(this)
• Validable
validate
• validate
74. class User(val name: String, val age: Int) : Validable
class Todo(val body: String) : Validable
Validations {
define<User> {
"name"{
be { name.length >= 5 } not "5文字以上入力"
be { name.length <= 10 } not "10文字以下で入力"
}
"age"{
be { age >= 0 } not "入力してください"
be { age >= 20 } not "20歳以上で入力"
}
}
define<Todo> {
"body"{
be { body.isNotBlank() } not "入力してください"
}
}
}
User("k",1).validate()
Todo("").validate()
75. interface Validable<T> {
val key: String
fun validate(): Map<String, List<String>> =
(Validations.get(key) as Validation<T>).validate(this as T)
}
}
inline fun <reified T> Validable<T>.key() =
Validations.key<T>()
class User(val name: String, val age: Int) : Validable<User> {
override val key: String = key()
}
User("k", 3).validate()
77. (Delegated Properties)
class Delegate : ReadWriteProperty<Hoge, String> {
var value = ""
override fun getValue(thisRef: Hoge, property: KProperty<*>): String {
return "value:" + value
}
override fun setValue(thisRef: Hoge, property: KProperty<*>, value: String) {
this.value = value
}
}
class Hoge {
var x by Delegate()
}
fun main() {
val hoge = Hoge()
hoge.x = "test"
print(hoge.x) // value: test
}
78. (Delegated Properties)
object ValidableDelegate {
class Validable<T>(private val key: String)
: ReadOnlyProperty<Any, Validation<T>> {
override fun getValue(thisRef: Any, property: KProperty<*>)
: Validation<T> {
return Validations.get(key) as Validation<T>
}
}
inline fun <reified T> get() = Validable<T>(Validations.key<T>())
}
class Todo(val body: String) {
private val validation by ValidableDelegate.get<Todo>()
fun validate() = validation.validate(this)
}
Todo("").validate()