Scala Currying и функциональные литералы

Я читал руководство-неофиты по лестнице-10, где наткнулся на следующий код.

type EmailFilter = Email => Boolean

val minimumSize: Int => EmailFilter = n => email => email.text.size >= n

Я понял первую строку, в которой псевдоним типа EmailFilter создается для функции, которая принимает логическое значение возврата электронной почты. Но я не понимаю вторую строку, где мы берем адрес электронной почты и номер в качестве ввода и возвращаем логическое значение, проверяя размер. Пожалуйста, расшифруйте вторую строку и объясните мне этот синтаксический сахарный код для функции.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
170
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Нет никакого синтаксического сахара, только сырые лямбда-выражения. Если вы вставите определение type EmailFilter в тип во второй строке, вы получите

Int => (Email => Boolean)

что то же самое (из-за правоассоциативности =>) как

Int => Email => Boolean 

и это прекрасно соответствует

n   => email => (email.text.size >= n)

который по сути просто говорит: учитывая число n, создайте фильтр электронной почты, который для данного email возвращает true тогда и только тогда, когда длина сообщения электронной почты не менее n., так что, например,

minimumSize(100)

возвращает закрытие, которое ведет себя так же, как

email => email.text.size >= 100

то есть он фильтрует все электронные письма, длина которых больше или равна 100. Таким образом, с подходящими примерами писем shortMail и longMail, вы получите:

minimumSize(100)(shortMail) // false
minimumSize(100)(longMail) // true

Функция minimumSize - это функция карри.

Каррирование - это способ разделить вызов функции на несколько последовательных вызовов подфункций.

У функции каррирования есть много хороших преимуществ, одно из которых состоит в том, что она позволяет вашей функции быть более компонуемой, откладывая реальный источник данных.

Изобразим использование:

n => email => email.text.size >= n   

Сначала мы можем вызвать эту функцию, передав параметр только для n:

minimumSize(2) // partially applies the minimumSize function with 2 as n 

Вы получите в это время:

val nextFunction = email => email.text.size >= 2

Затем вы звоните nextFunction по электронной почте:

nextFunction(Email("[email protected]"))

В этот раз вы получите логическое значение:

val bool = Email("[email protected]").text.size >= 2

Итак, если подвести итог:

Мы начали с Int, затем с Email, затем с Boolean:

Int => Email => Boolean

И, внимательно посмотрев на эту подпись, вы узнаете подпись EmailFilter. Подставим:

Int => EmailFilter 

Идея состоит в том, чтобы заставить EmailFilter действовать как шаблон, который вы можете параметризовать с помощью некоторых более высоких функций. Здесь мы параметризовали сравнение размера текста электронной почты, чтобы мы могли оставить EmailFilter общим.

Имейте в виду, что функциональное программирование - это создание функций.

Что такое '[email protected]'? Строковый литерал Python? Кроме того, у String нет члена .text.

Andrey Tyukin 17.05.2018 16:21

Но String по-прежнему не имеет члена .text, и тип Email не то же самое, что тип String для всех значимых определений типа Email. Не могли бы вы представить case class Email(text: String), чтобы сделать весь пример компилируемым?

Andrey Tyukin 17.05.2018 16:23

Это концептуальная подстановка (как псевдокод), цель не в том, чтобы сделать компилятор счастливым, а в простом объяснении идеи.

Mik378 17.05.2018 16:24

Хорошо, тогда, может быть, ради концептуальной простоты можно было бы просто удалить .text из email.text.size и набрать type Email = String.

Andrey Tyukin 17.05.2018 16:25

ха-ха, я как раз собирался заменить String на формулировку Email (я забыл об одном случае), но вы были быстрее :)

Mik378 17.05.2018 16:31

Mik378 Ага, спасибо, теперь яснее. Если бы я действительно хотел придираться, я бы сказал, что "[email protected]" больше похож на адрес, чем на текст, но ладно, теперь его, очевидно, можно расширить до компилируемого кода, (+1);)

Andrey Tyukin 17.05.2018 16:33

Да спасибо :) Адрес потому что речь идет о рассылке, но согласился, что text не особо подходит.

Mik378 17.05.2018 16:35

На самом деле это не каррированная функция, это просто функция высшего порядка (HOF), то есть функция, которая возвращает функцию. Предполагается, что EmailFilter будет вызываться несколько раз, по одному на каждое электронное письмо. При каррировании вы преобразуете одну функцию с несколькими аргументами в несколько функций с одним аргументом. Когда вы вызываете каррированную функцию, вы вызываете все функции по очереди, вы не используете повторно никакие промежуточные функции.

Tim 17.05.2018 17:40
minimumSize по определению является каррированным, даже если предполагается использовать его как функцию высшего порядка.
Mik378 17.05.2018 17:40

@ Mik378 Мне было бы интересно увидеть это определение. Если это каррированная функция, то незамещенной функцией будет minimumSize(Int, Email): Boolean, которая имеет неудачное название, потому что это просто операция сравнения. Я не верю, что функция предназначалась для вызова как minimumSize(10)(email), а скорее как emails.filter(minimumSize(10)), и поэтому я не верю, что это функция каррирования. YMMV

Tim 17.05.2018 17:54
hughfdjackson.com/javascript/why-curry-helps Не путайте намерение и природу.
Mik378 17.05.2018 17:56

@ Mik378 Я не запутался, я просто не хочу, чтобы люди думали, что любая функция с одним аргументом, возвращающая функцию с одним аргументом, по определению является каррированной версией функции с двумя аргументами. Описание функции как curried подразумевает намерение, и я не вижу никаких доказательств этого намерения. YMMV

Tim 17.05.2018 18:20

@ Тим Это карри. Взгляните на это видео Рунара Бьярнасона: youtu.be/ZasXwtTRkio ровно в 3:50 и послушайте, что он говорит в этот момент. (между 3:50 и 3:55). Кстати, то, что делает ваш emails.filter (minimumSize (10)), - это curring, поскольку вызов minimumSize (10) (currentEmail) будет происходить внутри.

Mik378 17.05.2018 18:24

@ Mik378 Я посмотрел. То, что докладчик называет каррированием, на самом деле является частичным применением, поэтому я не уверен, как это помогает.

Tim 17.05.2018 19:05

Цель каррирования - отложить выполнение функции. Это похоже на частичное применение функции, возвращающей 1-унарную вложенную функцию. Каррирование всегда создает вложенные унарные (одномерные) функции. Преобразованная функция остается в основном такой же, как и оригинал. Частичное приложение производит функции произвольной степени. Преобразованная функция отличается от исходной - ей нужно меньше аргументов.

Mik378 17.05.2018 19:18

Другие вопросы по теме