Хобрук: Ваш путь к мастерству в программировании

Когда Scala нужны типы параметров для анонимных и расширенных функций?

Когда компилятору Scala действительно нужна информация о типе параметров анонимных функций?

Например, учитывая эту функцию:

def callOn[T,R](target: T, f: (T => R)) = f(target)

то я не могу использовать его так:

callOn(4, _.toString)
  => error: missing parameter type for expanded function ((x$1) => x$1.toString)

и я должен указать

callOn(4, (_: Int).toString)

что довольно некрасиво. Почему мой пример не работает, тогда как такие методы, как map, filter, foldLeft и т. д. в классах коллекций, похоже, не нуждаются в этом явном типе?


Ответы:


1

Хитрость вывода типов состоит в том, чтобы рассматривать его как процесс итеративного уточнения. Каждый блок параметров можно использовать для вывода некоторых параметров типа, которые затем можно использовать в последующих блоках. Итак, возьмите следующее определение:

def chain[T,A,B](x: T)(fn1: T=>A)(fn2: A=>B) = fn2(fn1(x))

называется как:

chain(2)(_*10)("xxx"+_)

Так как это выводится? Во-первых, мы начинаем с блока (2), который, как известно, имеет тип Int. Подставив это обратно в параметр T, мы получим:

def chain[A,B](x: Int)(fn1: Int=>A)(fn2: A=>B) = fn2(fn1(x))

Следующий блок параметров — (_*10), где теперь мы знаем, что тип заполнителя _ равен Int... и умножение Int на Int дает еще Int. Это верно для возвращаемого типа, даже если происходит переполнение; в крайнем случае он может генерировать исключение, но исключения имеют тип Nothing, который является подклассом всего остального в системе типов, поэтому мы все еще можем сказать, что Nothing является Int, а выведенный тип Int все еще действителен.

С выведенным A метод становится следующим:

def chain[B](x: Int)(fn1: Int=>Int)(fn2: Int=>B) = fn2(fn1(x))

Остается только B, который можно вывести из ("xxx"+_). Поскольку String + Int является String, метод теперь такой:

def chain(x: Int)(fn1: Int=>Int)(fn2: Int=>String) = fn2(fn1(x))

Поскольку тип возвращаемого значения метода исходит непосредственно из fn2, это также можно явно показать для полноты картины:

def chain(x: Int)(fn1: Int=>Int)(fn2: Int=>String): String = fn2(fn1(x))

Вот и все, все типы безопасно разрешены, и метод доказал свою статическую валидность.


В вашем случае вам нужно вывести тип T, прежде чем можно будет вывести R из типа T=>R. Для этого вы должны разделить параметры на два отдельных блока, написав метод в каррированном виде:

def callOn[T,R](target: T)(f: (T => R)) = f(target)
05.02.2011

2

На этот вопрос также был дан ответ здесь:

Передача функций для всех применимых типов

Вы ожидаете, что компилятор Scala будет дважды учитывать оба параметра, чтобы вывести правильные типы. Однако Scala этого не делает — она использует информацию только из одного списка параметров в другой, но не из одного параметра в другой. Это означает, что параметры f и a [WS: цель и f в данном случае] анализируются независимо, без возможности узнать, что представляет собой другой.

Обратите внимание, что он действительно работает с каррированной версией:

scala> def callOn[T,R](target: T)(f: (T => R)) = f(target)
callOn: [T,R](target: T)(f: (T) => R)R

scala> callOn(4)(_.toString)
res0: java.lang.String = 4

scala> 
04.02.2011
  • Спасибо за Ваш ответ. Предположим, у меня есть более простой случай: def callOn4[R](f: (Int => R)) = f(4). Почему я могу опустить здесь тип параметра, но не могу, если я определяю его как def callOn4(f: (Int => _)) = f(4)? 04.02.2011
  • @jpp - просто попробуйте решить, какой тип возврата будет выводиться во второй версии... 04.02.2011
  • Новые материалы

    Не зря же это называют интеллектом
    Стек — C#, Oracle Опыт — 4 года Работа — Разведывательный корпус Мне пора служить Может быть, я немного приукрашиваю себя, но там, где я живу, есть обязательная военная служба на 3..

    LeetCode Проблема 41. Первый пропущенный положительный результат
    LeetCode Проблема 41. Первый пропущенный положительный результат Учитывая несортированный массив целых чисел, найдите наименьшее пропущенное положительное целое число. Пример 1: Input:..

    Расистский и сексистский робот, обученный в Интернете
    Его ИИ основан на предвзятых данных, которые создают предрассудки. Он словно переходит из одного эпизода в другой из серии Черное зеркало , а вместо этого представляет собой хронику..

    Управление состоянием в микрофронтендах
    Стратегии бесперебойного сотрудничества Микро-фронтенды — это быстро растущая тенденция в сфере фронтенда, гарантирующая, что удовольствие не ограничивается исключительно бэкэнд-системами..

    Декларативное и функциональное программирование в стиле LINQ с использованием JavaScript с использованием каррирования и генератора ...
    LINQ - одна из лучших функций C #, которая обеспечивает элегантный способ написания кода декларативного и функционального стиля, который легко читать и понимать. Благодаря таким функциям ES6,..

    Структуры данных в C ++ - Часть 1
    Реализация общих структур данных в C ++ C ++ - это расширение языка программирования C, которое поддерживает создание классов, поэтому оно известно как C с классами . Он используется для..

    Как я опубликовал свое первое приложение в App Store в 13 лет
    Как все началось Все началось три года назад летом после моего четвертого класса в начальной школе. Для меня, четвертого класса, лето кажется бесконечным, пока оно не закончится, и мой отец..