3

Я работаю с множеством карриных функций, принимая аналогичные аргументы, но не совсем. По этой причине мне было бы очень полезно иметь способ выполнить транспозицию, приложение и состав n-го аргумента, а также «окончательный» результат. Пример:Операции по n-му аргументу в кардинальных функциях в scala

val f :X=>Y=>W=>Z 
def compose1[A](w :A=>Y) :X=>A=>W=>Z 
def transpose1 :X=>W=>Y=>Z 
def apply1(y :Y) :X=>W=>Z 

Это может быть легко достигнуто при фиксированном значении п с чем-то вроде этого:

implicit class Apply2[X, Y, Z](private val f :X=>Y=>Z) extends AnyVal { 
    def transpose :Y=>X=>Z = { y :Y => x :X => f(x)(y) } 
    def provide(y :Y) :X=>Z ={ x :X => f(x)(y) } 
    def compose[A](y :A=>Y) : X=>A=>Z = { x :X => a :A => f(x)(y(a)) } 
    def apply[A, B]()(implicit ev :Z <:< (A=>B)) :Apply3[X, Y, A, B] = new Apply3[X, Y, A, B]((x :X) => (y :Y) => ev(f(x)(y))) 
} 

Но, конечно, я не приветствую идею авторских & -pasting 22 версии этого класса. Я также могу довольно легко сделать это для последнего аргумента с классом типа, , но решение, которое было бы похоже на succint для обозначения подчеркивания scala для частичного применения не-кардной функции, ускользает от меня. Я чувствую, что это должно быть возможно достигнуть следующих:

val f :A=>B=>C=>D=>E=>F 
val c = f()().compose((x :X) => new C(x)) :A=>B=>X=>D=>E=>F 
val t = f()().transpose :A=>B=>D=>C=>E=>F 
val s = f()().set(new C()) :A=>B=>D=>E=>F 

через неявное преобразование к некоторому Apply, который обеспечивает рекурсивную apply() метод, возвращающий вложенную Apply экземпляр.

Когда все типы известны, грубое решение конвертирования в HList и обратно работает, но бесхарактерная зависимость - это немного обоюдоострый меч.

+0

Можете ли вы объяснить, что вы подразумеваете под «устанавливает ловушки в общий код»? По моему опыту, я всегда чувствую себя более «захваченным» неявными преобразованиями, чем решениями типа класса. –

+0

В первую очередь, зависимые от пути типы; столь же мощные, как и они, они также являются самым большим источником моего разочарования (и недоумением о ошибках компилятора в прошлом). Во-вторых, IntelliJ не очень хорошо понимает бесформенность. В-третьих, я тоже ... Не недостаток библиотеки, и я очень часто использую HLists, но я немного неохотно занимаюсь магией вокруг кода производственного цикла. – Turin

+0

Отредактировано замечание о бесформенном, поскольку я не понимал, что это стало неконструктивной критикой, пока проблема между стулом и клавиатурой ... – Turin

ответ

3

Хорошо, у меня все еще зуд, но я, наконец, понял! Тем не менее, самая сложная задача программирования я сделал некоторое время. Если у кого-то есть предложения по улучшению (включая именование, обозначение и вообще синтаксис), я все уши.

/** Represents a partially applied, curried function `F` which is of the form `... X => A`, 
    * where X is the type of the first argument after (partial) application. 
    * Provides methods for manipulating functions `F` around this argument. 
    * @tparam F type of the manipulated function in a curried form (non-empty sequence of single argument lists) 
    * @tparam C[G] result of mapping partial result `(X=>A)` of function `F` to `G`. 
    * @tparam X type of the argument represented by this instance 
    * @tparam A result type of function F partially applied up to and including argument X 
    */ 
abstract class Curry[F, C[G], X, A](private[funny] val f :F) { prev => 
    /** Result of partial application of this function F up to and including parameter `X`. */ 
    type Applied = A 
    /** Replace X=>A with G as the result type of F. */ 
    type Composed[G] = C[G] 
    /** A function which takes argument `W` instead of `X` at this position. */ 
    type Mapped[W] = Composed[W=>A] 


    /** Provide a fixed value for this argument, removing it from the argument list. 
     * For example, the result of `Curry{a :Any => b :Byte => c :Char => s"&dollar;a&dollar;b&dollar;c" }().set(1.toByte)` 
     * (after inlining) would be a function `{a :Any => c :Char => s"&dollar;a&dollar;{1.toByte}&dollar;c" }`. 
     */ 
    def set(x :X) :Composed[A] = applied[A](_(x)) 

    /** Change the type of this argument by mapping intended argument type `W` to `X` before applying `f`. 
     * For example, given a function `f :F &lt;:&lt; D=>O=>X=>A` and `x :W=>X`, the result is `{d :D => o :O => w :W => f(d)(o)(x(w)) }`. 
     */ 
    def map[W](x :W=>X) :Composed[W=>A] = applied[W=>A]{ r :(X=>A) => (w :W) => r(x(w)) } 

    /** Map the result of partial application of this function up to argument `X` (not including). 
     * For example, if `F =:= K=>L=>X=>A`, the result is a function `{k :K => l :L => map(f(k)(l)) }`. 
     * @param map function taking the result of applying F up until argument `X`. 
     * @return resul 
     */ 
    def applied[G](map :((X => A) => G)) :Composed[G] 

    /** If the result of this partial application is a function `A &lt;:&lt; Y=>Z`, swap the order of arguments 
     * in function `F` from `=>X=>Y=>` to `=>Y=>X=>`. 
     */ 
    def transpose[Y, Z](implicit ev :A<:<(Y=>Z)) :Composed[Y=>X=>Z] = applied[Y=>X=>Z] { 
     r :(X=>A) => y :Y => x :X => ev(r(x))(y) 
    } 


    /** Skip to the next argument, i.e return an instance operating on the result of applying this function to argument `X`. */ 
    def apply[Y, Z]()(implicit ev :this.type<:<Curry[F, C, X, Y=>Z]) = new NextArg[F, C, X, Y, Z](ev(this)) 

    /** Skip to the next argument, i.e return an instance operating on the result of applying this function to argument `X`. 
     * Same as `apply()`, but forces an implicit conversion from function types which `apply` wouldn't. 
     */ 
    def __[Y, Z](implicit ev :this.type<:<Curry[F, C, X, Y=>Z]) = new NextArg[F, C, X, Y, Z](ev(this)) 
} 


/** Operations on curried functions. */ 
object Curry { 
    type Self[G] = G 
    type Compose[C[G], X] = { type L[G] = C[X=>G] } 

    /** Extension methods for modifying curried functions at their first argument (and a source for advancing to subsequent arguments. */ 
    @inline def apply[A, B](f :A=>B) :Arg0[A, B] = new Arg0(f) 

    /** Implicit conversion providing extension methods on curried function types. Same as `apply`, but doesn't pollute namespace as much. */ 
    @inline implicit def ImplicitCurry[A, B](f :A=>B) :Arg0[A, B] = new Arg0(f) 

    /** Operations on the first argument of this function. */ 
    class Arg0[X, Y](x :X=>Y) extends Curry[X=>Y, Self, X, Y](x) { 

     def applied[G](map: (X=>Y) => G) :G = map(f) 
    } 

    class NextArg[F, C[G], X, Y, A](val prev :Curry[F, C, X, Y=>A]) extends Curry[F, (C Compose X)#L, Y, A](prev.f) { 

     override def applied[G](map: (Y => A) => G): prev.Composed[X => G] = 
      prev.applied[X=>G] { g :(X=>Y=>A) => x :X => map(g(x)) } 
    } 
} 


def f :Byte=>Short=>Int=>Long=>String = ??? 

import Curry.ImplicitCurry 

f.set(1.toByte) :(Short=>Int=>Long=>String) 
f.map((_:String).toByte) :(String=>Short=>Int=>Long=>String) 
f.__.set(1.toShort) :(Byte=>Int=>Long=>String) 
Curry(f)().map((_:String).toShort) : (Byte=>String=>Int=>Long=>String)