
// Частичное применение функций
class Partial<T>
fun <T> partial(): Partial<T> = Partial()
operator @Suppress("UNUSED_PARAMETER") 
fun <P1, P2, R> ((P1, P2) -> R).invoke(p1: P1, 
       p2: Partial<P2> = partial()): (P2) -> R {
    return { p2: P2 -> this(p1, p2) }
}
operator @Suppress("UNUSED_PARAMETER") 
fun <P1, P2, P3, R> ((P1, P2, P3) -> R).invoke(p1: P1, 
      p2: Partial<P2> = partial(), p3: Partial<P3> = partial()): (P2, P3) -> R {
    return { p2: P2, p3: P3 -> this(p1, p2, p3) }
}
operator @Suppress("UNUSED_PARAMETER") 
fun <P1, P2, P3, R> ((P1, P2, P3) -> R).invoke(p1: P1, p2: P2, 
      p3: Partial<P3> = partial()): (P3) -> R {
    return { p3: P3 -> this(p1, p2, p3) }
}
// Пример использования
val plus={a:Int,b:Int -> a+b} // функция с 2 параметрами
plus(5,15) // 20
plus(5)(15) // карринг тоже работает
val plus5=plus(5) // подстановка 1-го параметра, получаем функцию с 1 параметром
plus5(15) // 20

// Композиция функций
infix fun <X, Y, Z> ((Y) -> Z).compose(f: (X) -> Y): (X) -> Z {
    return { p: X -> this(f(p)) }
}
// Пример использования
val mul2 = { i: Int -> i * 2 }
// Новая функция определяется через операцию над функциями
val mul2andPlus5 = plus5 compose mul2
mul2andPlus5(2) // 9

// Конвейер
infix inline fun <P, R> P.pipe(t: (P) -> R): R = t(this)
// Использование конвейера
2 pipe mul2 pipe plus5 // 9
// То же, без использования конвейера
// Функция, которая применяется последней, указана первой
plus5(mul2(2)) 
