// Define a class named "Person"
class Person(val name: String) {
    // Define an infix member function "isFriendWith"
    infix fun isFriendWith(anotherPerson: Person): Boolean {
        // Just a simple example: two persons are friends if their names have the same length
        return this.name.length == anotherPerson.name.length
    }
}
// Define an infix extension function "hasSameLengthAs" for the String class
infix fun String.hasSameLengthAs(anotherString: String): Boolean {
    return this.length == anotherString.length
}
fun main() {
    val alice = Person("Alice")
    val bob = Person("Bob")
    val carol = Person("Carol")
    // Use the infix function "isFriendWith"
    val aliceAndBobAreFriends = alice isFriendWith bob
    val aliceAndCarolAreFriends = alice isFriendWith carol
    println("Alice and Bob are friends: $aliceAndBobAreFriends") // true
    println("Alice and Carol are friends: $aliceAndCarolAreFriends") // false
    // Use the infix extension function "hasSameLengthAs"
    val stringsHaveSameLength = "Kotlin" hasSameLengthAs "Python"
    println("Kotlin and Python have the same length: $stringsHaveSameLength") // true
}
독립적인 외부 DSL과는 다르게 범용언어로 작성된 프로그램 일부인 내부 DSL
// 이런식으로 sql문을 내부 DSL 로 작성 가능  결과가 네이티브 코틀린 객체 .
(Country join Customer)
   .slice(Country.name, Count(Customer.id))
   .selectAll()
   .groupBy(Country.name)
   .orderBy(Count(Customer.id), isAsc = false)
   .limit(1)
	```
## DSL 구조
람다 중첩이나 메소드 연쇄시키는 방식으로 구조를 만듬 .
여러 함수를 호출

코틀린으로 HTML 만들면 ?
타입 안정성 보장 , Td는 tr안에서만 작성 해야 되!
fun createAnotherTable() = createHTML().table {
   val numbers = mapOf(1 to "one", 2 to "two")
   for ((num, string) in numbers) {
       tr {
           td { +"$num" }
           td { +string }
       }
   }
}
 



   open class Tag
   class TABLE : Tag() {
       fun tr(init: TR.() -> Unit) {
           // Implementation
       }
   }
   class TR : Tag() {
       fun td(init: TD.() -> Unit) {
           // Implementation
       }
   }
   class TD : Tag()
open class Tag(val name: String) {
    private val children = mutableListOf<Tag>()
    protected fun <T : Tag> doInit(child: T, init: T.() -> Unit) {
        child.init()
        children.add(child)
    }
    override fun toString() = "<$name>${children.joinToString("")}</$name>"
}
fun table(init: TABLE.() -> Unit) = TABLE().apply(init)
class TABLE : Tag("table") {
    fun tr(init: TR.() -> Unit) = doInit(TR(), init)
}
class TR : Tag("tr") {
    fun td(init: TD.() -> Unit) = doInit(TD(), init)
}
class TD : Tag("td")
fun createTable() = table {
    tr {
        td { }
    }
}
fun main() {
    println(createTable())
}
fun dropdownExample() = createHTML().dropdown {
   dropdownButton { +"Dropdown" }
   dropdownMenu(
       item("#", "Action"),
       item("#", "Another action"),
       divider(),
       dropdownHeader("Header"),
       item("#", "Separated link")
   )
}기존 HTML 에서 불필요한 세부사항을 제거하고 함수로 감춰둠.
객체를 함수처럼 호출해보자
class Greeter(val greeting: String) {
 operator fun invoke(name: String) {
     println("$greeting, $name!")
 }
}
val bavarianGreeter = Greeter("Servus")
bavarianGreeter("Dmitry") // Output: Servus, Dmitry!
함수 앞에 operator 변경자를 붙은 Invoke 메서드를 객체를 함수처럼 호출가능
Invoke 관례와 함수 타입
// Define a simple lambda
val lambda = { x: Int, y: Int -> x + y }
// Call the lambda as if it were a function
val sum = lambda(3, 4)
println("Sum of 3 and 4 is: $sum") // Output: Sum of 3 and 4 is: 7
// Define a custom class with an 'invoke' operator function
class Multiplier(val factor: Int) {
    operator fun invoke(x: Int): Int {
        return x * factor
    }
}
// Create an instance of the custom class
val triple = Multiplier(3)
// Call the instance as if it were a function
val result = triple(5)
println("Triple of 5 is: $result") // Output: Triple of 5 is: 15
        data class Issue(
            val id: String,
            val project: String,
            val type: String,
            val priority: String,
            val description: String
        )
        class ImportantIssuesPredicate(val project: String) : (Issue) -> Boolean {
            override fun invoke(issue: Issue): Boolean {
                return issue.project == project && issue.isImportant()
            }
            private fun Issue.isImportant(): Boolean {
                return type == "Bug" && (priority == "Major" || priority == "Critical")
            }
        }
        val i1 = Issue(
            "IDEA-154446",
            "IDEA",
            "Bug",
            "Major",
            "Save settings failed"
        )
        val i2 = Issue(
            "KT-12183",
            "Kotlin",
            "Feature",
            "Normal",
            "Intention: convert several calls on the same receiver to with/apply"
        )
        val predicate = ImportantIssuesPredicate("IDEA")
        for (issue in listOf(i1, i2).filter(predicate)) {
            println(issue.id)
        }  ```
  data class Person(val name: String, val age: Int)
  class AgeFilter(val minAge: Int, val maxAge: Int) : (Person) -> Boolean {
      override fun invoke(person: Person): Boolean {
          return person.isWithinAgeRange()
      }
      private fun Person.isWithinAgeRange(): Boolean {
          return age in minAge..maxAge
      }
  }
  fun main() {
      val people = listOf(
          Person("Alice", 30),
          Person("Bob", 25),
          Person("Charlie", 40),
          Person("David", 20)
      )
      val ageFilter = AgeFilter(25, 35)
      val filteredPeople = people.filter(ageFilter)
      println(filteredPeople)
  }dependencies {
compile("junit:junit:4.11")
}
class DependencyHandler {
    fun compile(coordinate: String) {
        println("Added dependency on $coordinate")
    }
    operator fun invoke(body: DependencyHandler.() -> Unit) {
        body()
    }
}
val dependencies = DependencyHandler()
dependencies.compile("org.jetbrains.kotlin:kotlin-stdlib:1.0.0") // Output: Added dependency on org.jetbrains.kotlin:kotlin-stdlib:1.0.0
dependencies {
    compile("org.jetbrains.kotlin:kotlin-reflect:1.0.0")
} // Output: Added dependency on org.jetbrains.kotlin:kotlin-reflect:1.0.0
complie함수도 사용가능하고 , 여러개의 람다 인자를 넘겨주는것을 가능하게하는 구현
두번째 코드는 결과?
dependencies.invoke {
this.compile("org.jetbrains.kotlin:kotlin-reflect:1.0.0")
}
s should startwith(“kot”)
infix fun T.should(matcher: Matcher) = matcher.test(this)
interface Matcher<T> {
    fun test(value: T)
}
class StartWith(val prefix: String) : Matcher<String> {
    override fun test(value: String) {
        if (!value.startsWith(prefix)) {
            throw AssertionError("String $value does not start with $prefix")
        }
    }
}
이런식으로 구현가능한게 신기하다. 좀더 익숙해지게 공부를 하면 뭔가 더 좋은 코드를 짤 수 잇을거같다
아직까지 100퍼 완전 내것이 된건아니지만 코드를 보니 저렇게도 짤수잇구나 신기하다
// "kotlin”. should(start).with("kot")
object Start
infix fun String.should(x: Start): StartWrapper = StartWrapper(this)
class StartWrapper(val value: String) {
  infix fun with(prefix: String) {
      if (!value.startsWith(prefix)) {
          throw AssertionError("String does not start with $prefix: $value")
      } else {
          Unit
      }
  }
}이런식으로 중위연산자를 활용하면 테스트 코드를 좀더 명확히? 짤 수 있게된다.
한번 이런식으로 짜두는 것도 좋을듯.. 연습하자