Типы высшего порядка в Scala

Я читаю книгу «Функциональное программирование в Scala», а в главе «Моноиды» говорится об интерфейсе Monoid, который выглядит следующим образом:

trait Monoid[A] {
 def op(a1: A, a2: A): A
 def zero: A
}

Позже они определяют конкретные экземпляры Monoid, расширяя этот интерфейс. Например.,

val intMonoid = new Monoid[Int] {
  ...
}

val listMonoid = new Monoid[List[Int]] {
  ...
}

Еще на паре страниц, которые я прочитал в этой главе 10, я наткнулся на «высокородные типы», которые, согласно книге, относятся к любому типу, который сам по себе является типом, который может принимать другие типы.

trait Foldable[F[_]] {
 ...
 ...
}

Таким образом, черта Складная согласно книге относится к более высокородным типам. Мой вопрос в том, что Monoid [A] для меня также соответствует определению «высший родственный тип», поскольку он может принимать List [A]. Я правильно понимаю? Если нет, что делает более высокодородные типы более высокодородными типами в Scala?

Изменить: Итак, конструктор унарного типа принимает аргумент и создает тип. А как насчет этого дела?

def listMonoid[A] = new Monoid[List[A]] {
  ...
  ...
}

Так является ли моя функция listMonoid HKT?


person joesan    schedule 17.04.2017    source источник
comment
Этот вопрос объясняет более высокодородные типы простыми словами, которые дают точный ответ что вы спрашиваете.   -  person Yuval Itzchakov    schedule 17.04.2017


Ответы (1)


Немного терминологии:

  • правильный тип (например, Int)
  • тип первого порядка (например, Список [_]); мы могли бы также сказать первоклассный вид
  • высший тип (например, Монада [M [_])

Когда ты говоришь

trait Monoid[A] {
  def op(a1: A, a2: A): A
  def zero: A
}

val listMonoid = new Monoid[List[Int]] {
  def op(l: List[Int], l2: List[Int]) =  List(1,2)
  def zero = List(1,2)
}

вы параметризуете черту Monoid с помощью некоторого типа A, который может (как вы заметили) быть простым типом, также известным как правильный тип (например, Int) или параметризованный тип (например, List[Int] или даже List[Set[Map[Int, Int]]). Это делает Monoid типом первого порядка. Мы также можем сказать, что это конструктор унарного типа - для создания окончательного типа требуется один тип.

В отличие от Monoid, некоторые абстракции (например, Monad) должны быть параметризованы конструктором типа. Int больше не работает. Это должен быть «какой-то тип, который может произвести другой тип». Абстракция, параметризованная конструктором типа (то есть параметризованная «типом первого порядка»), является типом более высокого порядка. Вот пример:

trait Monad[M[_]] {
  def op[A, B](m: M[A], f: A => M[B]): M[B]
  def zero[A](a: A): M[A]
}

object ListMonad extends Monad[List] {
  def op[A, B](m: List[A], f: A => List[B]) = m.flatMap(f)
  def zero[A](a: A) = List[A](a)
}

val listMonad = ListMonad.zero(42)
val result = ListMonad.op(listMonad, (i: Int) => List(i - 1, i, i + 1))

// result = List(41, 42, 43)

Итак, Monad параметризуется типом первого порядка (конструктор унарного типа), что делает сам Monad типом более высокого порядка.

Обратите внимание, что Monad на самом деле не заботится о самом «внутреннем типе» на уровне класса, поскольку он будет определяться методами op и zero. Вы также можете сказать trait Monad[M[A]] и «исправить» тип A в точке определения класса ListMonad (например, исправить его до Int), но тогда вы теряете гибкость (тогда ваш ListMonad сможет построить и отобразить только List[Int] и вы потребуется другой класс, скажем, для List[String]).

Это отличается от моноида, который не относится к более высокородным типам; ему не нужен конструктор типа для создания типа. Если бы это было необходимо, у вас никогда не было бы, скажем, Monoid[Int], потому что Int не является конструктором типа.

Также обратите внимание, как я сказал, что Monad нужен конструктор типа унарный, то есть он принимает только один тип (в отличие, например, от Map, который принимает два). Конструкторы типов часто обозначаются звездочками и стрелками:

  • унарный конструктор типа первого порядка - * -> * (он принимает единственный тип и производит окончательный тип, например Set)
  • конструктор двоичного типа первого порядка * -> * -> * (конструктор двоичного типа принимает два типа для создания окончательного типа, например, Map)
  • унарный тип более высокого порядка - (* -> *) -> * (для создания окончательного типа требуется один конструктор унарного типа, например, Monad)

и т.п.

Итак, тип первого порядка берет простой / конкретный / правильный тип и производит окончательный тип, тогда как тип более высокого порядка идет на один уровень выше; для создания окончательного типа требуется тип первого порядка.

РЕДАКТИРОВАТЬ:

Отвечая на ваш вопрос в части «редактировать»: Хорошо, я думаю, я знаю, что вас смущает. listMonoid - это не тип, поэтому он не может быть типом более высокого порядка. Это метод. Monad[List[Int]] - полностью разрешенный тип. Monad[F[A]] тоже полностью решен. Однако Monad сам по себе относится к типу более высокого порядка.

Позвольте мне провести параллель с функциями. Если у вас есть функция foo(x: Int), то вызовы функций, такие как foo(42) или foo(someIntegerValue), приводят к конкретным значениям. Это аналог Monad[List[Int]] и Monad[F[A]]. Однако foo сам по себе является функцией, так же как Monad сам является конструктором типа.

Если foo принимает простое значение (не функцию), это функция первого порядка; если он принимает или возвращает функцию, то это функция высшего порядка. То же самое с конструкторами типов. Если он принимает простой тип, это конструктор типа первого порядка. Пример: List. Если требуется другой конструктор типа, это конструктор типа более высокого порядка (также известный как тип более высокого порядка). Пример: Monad.

Не смешивайте разрешенные типы с конструкторами типов. Имеет смысл задуматься, является ли функция foo старшей или нет; это зависит от его аргументов и типа возвращаемого значения. Но нет смысла думать, является ли foo(42) высшим порядком или нет; это не функция, а приложение функции, результатом которого является значение. Monad[List[Int]] - это не конструктор типа, а приложение конструктора типа List к конструктору типа Monad (который является более высоким порядком). Точно так же Monoid[List[Int]] не является конструктором типа, а приложением типа List[Int] к конструктору типа Monoid (который является первым порядком). Конструкторы типов высшего порядка называются HKT. Нет смысла говорить о HKT и указывать на конкретный разрешенный тип (который был создан в результате применения некоторого конструктора типа).

person slouc    schedule 17.04.2017
comment
Я все еще не мог заметить тонкости, которая отличает более высокодородный тип от примера с моноидом, который у меня есть! - person joesan; 17.04.2017
comment
Монада никогда не могла работать только с M, ей нужны либо M [_], либо M [B], но с последним вы теряете гибкость. Бывший позволяет вам сказать какой-то конструктор типа, оставляя фактическое решение о конкретном типе кому-то другому (в нашем примере это методы op и zero). Конструктор этого типа называется HKT. С другой стороны, для моноида требуется только M; если вы попытаетесь заставить Monoid использовать HKT, у вас никогда не будет Monoid[Int]. - person slouc; 17.04.2017
comment
@sparkr Есть разница. Вы можете иметь Monoid[List[Int]], но не Monoid[List]. Вы можете иметь Monad[List], но не Monad[List[Int]]. Monoid[List[Int]] работает только для списков целых чисел. Monad[List] работает для любого списка, независимо от того, что внутри. - person Jasper-M; 17.04.2017
comment
@Yuval Ты прав, я испортил терминологию в этой части. См. Редактировать. - person slouc; 17.04.2017
comment
@slouc Ура :) - person Yuval Itzchakov; 17.04.2017
comment
Эй, я думал вопрос закрыт. Я ответил на вашу часть редактирования. - person slouc; 18.04.2017
comment
Я говорю не о Monoid [List [Int]], а о Monoid [List [A]], так является ли моя черта Monoid HKT? - person joesan; 18.04.2017
comment
Неважно. Это как сказать, что я говорю не о foo (42), а о foo (someVariableWhichMayBeOfAnyValueAtRuntime). Не функции, а вызовы функций. Monoid - конструктор типа. Monoid[SomeType] - конкретный тип. Как только вы предоставите Monoid тип, он создаст конкретный тип. Предоставляемый вами тип может быть List[Int], List[A], F[A], List[Set[Option[Int]]] или любым другим. Всегда обращайте внимание на определение для Monoid. Если он определен как Monoid[A], это конструктор типов первого порядка (унарный). Если это Monoid[F[A]] или Monoid[F[_]], это HKT. - person slouc; 18.04.2017
comment
Посмотрите на строчку в моем ответе object ListMonad extends Monad[List]. Видишь там List? Это конструктор типа. Там не было ни List[Int], ни List[A], ни List[Set[Option[Whatever]]]. Это все приложения конструктора типа. Но монада не параметризуется конкретным типом; он параметризуется конструктором типа первого порядка. Следовательно, мы предоставляем ему только список. - person slouc; 18.04.2017
comment
Это прояснило мои сомнения! - person joesan; 18.04.2017
comment
@sparkr Также спасибо за вопрос (даже если это дубликат), потому что я также прояснил некоторые вещи со своей стороны, о которых я никогда не догадывался, я не был на 100% ясен. Нам потребовалось много написания, редактирования и комментариев, но я думаю, что сейчас у нас все хорошо? :) - person slouc; 18.04.2017
comment
Мы определенно, и я благодарю вас за подробности! - person joesan; 18.04.2017