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

Scala — Реализация полиморфного абстрактного признака без полиморфизма

Я разрабатываю простой конвейер обработки данных в Scala. В нем участвуют PipelineStage, которые transform некоторые StageOutput превращают в другие StageOutput. Pipeline — это оболочка для последовательности PipelineStage, которая должна иметь общий доступ ко всем их методам transform.

Однако у меня возникли проблемы, поскольку оба решения, которые я придумал, принципиально не работают... первое основано на абстрактном полиморфном методе, реализуемом неполиморфным способом (не компилируется), а второе полагается на возможность использовать Seq[AbstractTrait] там, где AbstractTrait является полиморфным, что опять же бессмысленно для компилятора. Смотрите следующим образом...

Сценарий 1. Сделать метод transform полиморфным.

    abstract trait PipelineStage {
        def transform[A <: StageOutput, B <: StageOutput](in: A): B
    }

    class PipelineStage2 extends PipelineStage {
        def transform(in: StageOutput1): StageOutput2
    }

    class Pipeline {
        def stages: Seq[PipelineStage]
    }

Здесь у Pipeline нет проблем с компиляцией, но этапы не будут компилироваться, поскольку сигнатуры их transform методов, хотя они и «уважают» полиморфизм абстрактной сигнатуры, на самом деле сами по себе не полиморфны, поэтому не совпадают, насколько компилятор обеспокоен.

Сценарий 2. Сделать сам признак PipelineStage полиморфным.

    abstract trait PipelineStage[A <: StageOutput, B <: StageOutput] {
        def transform(in: A): B
    }

    class PipelineStage2 extends PipelineStage[StageOutput1, StageOutput2] {
        def transform(in: StageOutput1): StageOutput2
    }

    class Pipeline {
        def stages: Seq[PipelineStage]
    }

Это решает проблему для PipelineStage, у них нет проблем с компиляцией, и их методы преобразования прекрасно работают сами по себе. Однако сейчас они технически не реализуют один и тот же трейт, поэтому Pipeline не компилируется, так как Seq[PipelineStage] теперь бессмысленно...

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


  • Попробуйте заполнить тело метода Pipeline, предполагая, что вы каким-то образом скомпилировали его и имеете доступ к def stages: Seq[PipelineStage]. Покажите, как именно вы планируете использовать эту функцию. 02.07.2016

Ответы:


1

Первый вообще не делает то, что вы хотите: StageOutput1 и 2 в def transform[StageOutput1, StageOutput2](in: StageOutput1): StageOutput2 — это просто имена параметров, это означает то же самое, что и def transform[A, B](in: A): B.

Мое предложение было бы использовать второе, но вместо class Pipeline вы можете добавить

def compose[C <: StageOutput](other: PipelineStage[B, C]): PipelineStage[A, C] = new PipelineStage[A, C] {
  def transform(in: A) = other.transform(PipelineStage.this.transform(in))
}

и создайте конвейер, который вы хотите, при условии, что типы действительно совпадают.

(Кроме того, ваш PipelineStage в основном представляет собой Function1 с ограничением; если вы не планируете добавлять другие методы или намеренно ограничивать типы этапов, которые могут быть написаны, вы можете просто использовать функции и воспользоваться преимуществами стандартного библиотека!)

02.07.2016
  • Вы абсолютно правы, отредактировано для удаления полиморфизма в методе PipelineStage1 первого сценария transform. Была опечатка, а не то, что я пытался скомпилировать. Кроме того, у меня есть несколько других методов и атрибутов для PipelineStage, поэтому мне нужно использовать трейт. Однако я как-то не задумывался о том, что между PipelineStage и Pipeline нет принципиальной разницы. Я могу поместить все методы, которые я хотел, на Pipeline на PipelineStage и использовать вашу функцию составления. Спасибо! 02.07.2016
  • На самом деле, я бы предложил называть его Pipeline вместо PipelineStage. 02.07.2016
  • Новые материалы

    Как сделать HTML динамическим с помощью JavaScript
    Код JavaScript выполняется внутри страниц сайта. Таким образом, страница вашего сайта содержит метки HTML, а также пояснения (скрипты), составленные с использованием диалекта сценариев, такого как..

    Деревья классификации и регрессии
    Это мой второй пост об алгоритмах машинного обучения. Мой первый пост посвящен искусственным нейронным сетям, вы можете найти его ниже. Нейронные сети — базовое..

    HMTL - Многозадачное обучение для решения задач НЛП
    Достижение результатов SOTA путем передачи знаний между задачами Область обработки естественного языка включает в себя десятки задач, среди которых машинный перевод, распознавание именованных..

    Решения DBA Metrix
    DBA Metrix Solutions предоставляет удаленного администратора базы данных (DBA), который несет ответственность за внедрение, обслуживание, настройку, восстановление базы данных, а также другие..

    Начало работы с Блум
    Обзор и Codelab для генерации текста с помощью Bloom Оглавление Что такое Блум? Некоторые предостережения Настройка среды Скачивание предварительно обученного токенизатора и модели..

    Создание кнопочного меню с использованием HTML, CSS и JavaScript
    Вы будете создавать кнопочное меню, которое имеет состояние наведения, а также позволяет вам выбирать кнопку при нажатии на нее. Финальный проект можно увидеть в этом Codepen . Шаг 1..

    Внедрите OAuth в свои веб-приложения для повышения безопасности
    OAuth — это широко распространенный стандарт авторизации, который позволяет приложениям получать доступ к ресурсам от имени пользователя, не раскрывая его пароль. Это позволяет пользователям..