Ответ 1
Вот более простой пример:
def mysteryMethod[A, B](somePair: (A, B)): B = ???
Что делает этот метод? Оказывается, есть только одно, что может сделать этот метод! Вам не нужно имя метода, вам не нужна реализация метода, вам не нужна какая-либо документация. Тип говорит вам все, что он мог бы сделать, и оказывается, что "все" в этом случае - это одно.
Итак, что он делает? Он принимает пару (A, B)
и возвращает некоторое значение типа B
. Какую ценность он возвращает? Может ли он построить значение типа B
? Нет, не может, потому что он не знает, что такое B
! Может ли он вернуть случайное значение типа B
? Нет, потому что случайность является побочным эффектом и, следовательно, должна появляться в сигнатуре типа. Может ли он выйти во вселенной и получить некоторые B
? Нет, потому что это будет побочным эффектом и должно появиться в сигнатуре типа!
Фактически, единственное, что он может сделать, это вернуть значение типа B
, которое было передано в него, второй элемент пары. Таким образом, этот mysteryMethod
действительно является методом second
, и его единственная разумная реализация:
def second[A, B](somePair: (A, B)): B = somePair._2
Обратите внимание, что в действительности, поскольку Scala не является ни чистым, ни тотальным, на самом деле существует пара других вещей, которые может сделать метод: выкиньте исключение (т.е. возвратитесь ненормально), перейдите в бесконечный цикл (т.е. не возвращайся вообще), используйте рефлексию для определения фактического типа B
и рефлексивно вызовите конструктор для создания нового значения и т.д.
Однако, если принять чистоту (возвращаемое значение может зависеть только от аргументов), тотальность (метод должен возвращать значение обычно) и параметричность (он действительно ничего не знает о A
и B
), тогда на самом деле очень много можно рассказать о методе, только взглянув на его тип.
Вот еще один пример:
def mysteryMethod(someBoolean: Boolean): Boolean = ???
Что это может сделать? Он всегда может возвращать false
и игнорировать его аргумент. Но тогда это было бы слишком ограничено: если он всегда игнорирует свой аргумент, тогда ему все равно, что это Boolean
, и его тип скорее будет
def alwaysFalse[A](something: A): Boolean = false // same for true, obviously
Он всегда может просто вернуть свой аргумент, но опять же, на самом деле он не будет заботиться о логических значениях, и его тип скорее будет
def identity[A](something: A): A = something
Итак, действительно, единственное, что он может сделать, это вернуть другое логическое значение, чем тот, который был передан, и поскольку имеется только два логических элемента, мы знаем, что наш метод mysteryMethod, по сути, not
:
def not(someBoolean: Boolean): Boolean = if (someBoolean) false else true
Итак, здесь мы имеем пример, где типы не дают нам реализацию, но, по крайней мере, они дают в виде (небольшого) набора из 4 возможных реализаций, только один из которых имеет смысл.
(Кстати, оказалось, что существует только одна возможная реализация метода, который принимает A
и возвращает A
, и это метод идентификации, показанный выше.)
Итак, чтобы повторить:
- чистота означает, что вы можете использовать только строительные блоки, которые были переданы вам (аргументы)
- сильная, строгая система статического типа означает, что вы можете использовать только эти строительные блоки таким образом, чтобы их типы выстраивались в линию
- тотальность означает, что вы не можете делать глупые вещи (например, бесконечные циклы или исключения для исключения)
- Параметричность означает, что вы вообще не можете делать какие-либо предположения о своих переменных типа
Подумайте о своих аргументах как части машины и ваших типах в качестве разъемов на этих частях машины. Будет ограниченное количество способов соединения этих частей машины таким образом, чтобы вы только подключали совместимые разъемы, и у вас нет оставшихся частей. Достаточно часто, будет только один способ, или если есть несколько способов, тогда часто один будет, очевидно, правильным.
Это означает, что после того, как вы разработали типы своих объектов и методов, вам даже не придется думать о том, как реализовать эти методы, потому что типы уже будут диктовать единственный возможный способ их реализации! Учитывая, сколько вопросов в StackOverflow в основном "как это реализовать?", Можете ли вы представить себе, как освободить его от необходимости вообще не думать об этом, потому что типы уже диктуют одну (или одну из нескольких) возможных реализаций
Теперь посмотрите на подпись метода в своем вопросе и попробуйте по-разному объединить A
и f
таким образом, чтобы типы выстраивались в линию, и вы используете как A
, так и f
и вы действительно увидите, что есть только один способ сделать это. (Как показали Крис и Пол.)