В Scala можно использовать общие параметры типа с определениями * function *?

Есть ли синтаксис, позволяющий использовать параметры типового типа в литералах функций? Я знаю, что могу обернуть его в методе, например:

def createLongStringFunction[T](): (T) => Boolean = {
  (obj: T) => obj.toString.length > 7
}

но потом мне нужно вызвать метод для каждого типа T и получить новую функцию. Я просмотрел ссылку на язык, и, хотя я вижу, что синтаксис функции литерала переводится компилятором в экземпляр объекта Functionn, который сам имеет общие типы ввода, похоже, что магия компилятора реализует эти параметры во время создания. Я не нашел синтаксиса, который позволяет мне, по сути, "оставить один или несколько параметров типа Functionn unbound". Я бы предпочел что-то вроде:

// doesn't compile
val longStringFunction: [T](T) => Boolean = (obj: T) => obj.toString.length > 7

Есть ли такая вещь? Или в этом отношении, что является явным типом функции eta-расширения, когда расширяемый метод имеет общие параметры?

Это чисто надуманный и бесполезный пример. Конечно, я мог бы просто использовать функцию Any здесь.

Ответ 1

Нет, параметры типа применяются только к методам, а не к объектам функции. Например,

def f[T](x: T) = x     //> f: [T](x: T)T
val g = f _            //> g: Nothing => Nothing = <function1>
// g(2)                // error
val h: Int=>Int = f _  //> h  : Int => Int = <function2>
h(2)                   //> res0: Int = 2

Метод f не может быть преобразован в объект полиморфной функции g. Как вы можете видеть, предполагаемый тип g на самом деле Function1[Nothing, Nothing], что бесполезно. Однако с подсказкой типа мы можем построить h: Function1[Int,Int], который работает как ожидается для аргумента Int.

Ответ 2

Как вы говорите, в вашем примере все, что вам нужно, это метод toString, поэтому Any будет обычным решением. Тем не менее, есть призыв к тому, чтобы использовать типы более высокого ранга в ситуациях, таких как применение конструктора типа, такого как List, к каждому элементу в кортеже.

Как уже упоминалось в других ответах, прямой поддержки для этого нет, но есть относительно хороший способ его кодирования:

trait ~>[A[_],B[_]] {
  def apply[X](a : A[X]) : B[X]
}

type Id[A] = A //necessary hack

object newList extends (Id ~> List) {
  def apply[X](a : Id[X]) = List(a)
}

def tupleize[A,B, F[_]](f : Id ~> F, a : A, b : B) = (f(a), f(b))

tupleize(newList, 1, "Hello") // (List(1), List(Hello))

Ответ 3

Так как longStringFunction определяется следующим образом: значение, которое должно иметь некоторый заданный тип.

val longStringFunction: (T) => Boolean = (obj: T) => obj.toString.length > 7

Однако вы можете повторно использовать объект функции с помощью метода:

scala> val funObj: Any => Boolean = _.toString.size > 7
funObj: Any => Boolean = <function1>

scala> def typedFunction[T]: T => Boolean = funObj
typedFunction: [T]=> T => Boolean

scala> val f1 = typedFunction[String]
f1: String => Boolean = <function1>

scala> val f2 = typedFunction[Int]
f2: Int => Boolean = <function1>

scala> f1 eq f2
res0: Boolean = true

Это работает, потому что trait Function1[-T1, +R] является контравариантным типа T1.

Ответ 4

В scala значения функции являются параметрически мономорфными (в то время как методы являются полиморфными)

Бесполезная библиотека вводит значения полиморфной функции, которые могут отображаться по HLists и многим другим функциям.

Пожалуйста, рассмотрите следующие ссылки: http://www.chuusai.com/2012/04/27/shapeless-polymorphic-function-values-1/ http://www.chuusai.com/2012/05/10/shapeless-polymorphic-function-values-2/