Использование функции параметра mutli в качестве универсального типа другой функции scala

#scala

Вопрос:

У меня есть следующий код

 object Test {

  def bar: String => Double = {
    foo[String](_.toDouble)
  }

  def baz: (Double, Double) => Double = {
    foo[(Double, Double)] {
      case (d1, d2) => d1   d2
    }
  }

  def foo[T](f: T => Double): T => Double = {
    f
  }
}
 

бар работает без проблем, как и ожидалось. Я пытаюсь добиться аналогичной работы с функцией параметра mutli в качестве одного из входных данных, но это не работает, потому что scala рассматривает foo[(Double, Double)] его как тип кортежа, а не как параметр функции. Есть ли какой-нибудь способ сообщить scala, что это параметр функции, а не кортеж?

код: https://scastie.scala-lang.org/sCYyU6ziT3mKOhkBiofAaQ

Комментарии:

1. Ну, есть простое исправление baz типа: ((Double, Double)) => Double это функция одного аргумента (который является кортежем) . — Другой путь невозможен, АФАИК.

2. Это просто очень упрощенный пример в данном случае, ради самого вопроса. На самом деле в foo будет куча логики, которая дополнит исходную функцию

Ответ №1:

Я думаю, что причина, по которой это не сработало для вас, заключается в том, что у вас нет доступа к двум отдельным аргументам baz метода, которые необходимо упаковать в кортеж перед вызовом foo . (Я думаю, что есть некоторые дискуссии о возможности преобразования списков параметров в/из кортежей, но в настоящее время это невозможно.) Попробуйте вместо этого использовать это определение:

 object Test {
  def bar: String => Double = {
    foo[String](_.toDouble)
  }
  def baz: (Double, Double) => Double = {(p1, p2) =>
    foo[(Double, Double)] {
      case(d1, d2) => d1   d2
    }((p1, p2))
  }
  def foo[T](f: T => Double): T => Double = f
}
 

Если я правильно понимаю ваши намерения, это будет использоваться так (в Scala REPL):

 scala> Test.bar("5.0")
val res0: Double = 5.0
                                                                                                                                    
scala> Test.baz(3.0, 5.0)
val res1: Double = 8.0
 

Однако это кажется немного запутанным. Если bar и baz хранить ссылки на методы, то они должны быть объявлены val , а не def :

 object Test {
  val bar: String => Double = {
    foo[String](_.toDouble)
  }
  val baz: (Double, Double) => Double = {(p1, p2) =>
    foo[(Double, Double)] {
      case(d1, d2) => d1   d2
    }((p1, p2))
  }
  def foo[T](f: T => Double): T => Double = f
}
 

Который имеет тот же синтаксис использования и выдает тот же результат:

 scala> Test.bar("5.0")
val res2: Double = 5.0
                                                                                                                                    
scala> Test.baz(3.0, 5.0)
val res3: Double = 8.0
 

Но даже это все равно довольно неуклюже. bar является ссылкой на функцию , которая принимает a String и возвращает a Double , в то время baz как является ссылкой на функцию, которая принимает два Double s и возвращает a Double . Так почему бы просто не создавать bar и baz методы сами по себе, а не ссылки на методы? Это привело бы к:

 object Test {
  def bar(s: String): Double = foo[String](_.toDouble)(s)

  def baz(p1: Double, p2: Double): Double = {
    foo[(Double, Double)] {
      case(d1, d2) => d1   d2
    }((p1, p2))
  }

  def foo[T](f: T => Double): T => Double = f
}
 

который опять же используется таким же образом:

 scala> Test.bar("5.0")
val res4: Double = 5.0
                                                                                                                                    
scala> Test.baz(3.0, 5.0)
val res5: Double = 8.0
 

И, конечно, foo само по себе излишне, поскольку оно идентично его аргументу. Так что все это можно было бы написать гораздо проще, как просто:

 object Test {
  def bar(s: String): Double = s.toDouble

  def baz(p1: Double, p2: Double): Double = p1   p2
}