Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > Holy Wars > trait + impl vs class


Автор: D_KEY 03.06.15, 21:52
Предлагаю похоливарить на данную тему.

Коротко о trait'ах, impl'ах и данных на примере Rust.
Вместо "классического" ОО подхода, Rust предоставляет отдельные ортогональные механизмы.
Если классы у нас описывают и методы и данные, то в Rust мы задаем отдельно данные:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    struct Circle {
        x: f64,
        y: f64,
        radius: f64,
    }


и отдельно наборы методов:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    impl Circle {
        fn area(&self) -> f64 {
            std::f64::consts::PI * (self.radius * self.radius)
        }
    }

Можно задавать несколько блоков impl для типа.

Если классы предоставляют возможность наследования, то в Rust мы описываем интерфейсы посредством trait'а:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    trait HasArea {
        fn area(&self) -> f64;
    }


А реализацию trait'а для нашего типа описываем в соответствующем блоке impl.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    struct Circle {
        x: f64,
        y: f64,
        radius: f64,
    }
     
    impl HasArea for Circle {
        fn area(&self) -> f64 {
            std::f64::consts::PI * (self.radius * self.radius)
        }
    }
     
    struct Square {
        x: f64,
        y: f64,
        side: f64,
    }
     
    impl HasArea for Square {
        fn area(&self) -> f64 {
            self.side * self.side
        }
    }

Можно добавлять реализацию того или иного trait'а для того или иного типа, без внесения каких-либо изменений непосредственно в сам тип.

Наследование же поддерживается только на уровне наследование trait'ов:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    trait Foo {
        fn foo(&self);
    }
     
    trait FooBar : Foo {
        fn foobar(&self);
    }


Причем для реализующего trait типа это будет означать, что тип так же должен иметь impl и для "базового" trait'а:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    struct Baz;
     
    impl Foo for Baz {
        fn foo(&self) { println!("foo"); }
    }
     
    impl FooBar for Baz {
        fn foobar(&self) { println!("foobar"); }
    }


В trait'ах можно указывать реалиализацию методов по умолчанию:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    trait Foo {
        fn bar(&self);
     
        fn baz(&self) { println!("We called baz."); }
    }


Так же trait'ы используются для задания ограничений на generic-параметры, но это совсем другая история...

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

Небольшой пример с rustbyexample.com(предыдущие примеры были с doc.rust-lang.org):
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    trait Animal {
        // Static method signature; `Self` refers to the implementor type
        fn new(name: &'static str) -> Self;
     
        // Instance methods, only signatures
        fn name(&self) -> &'static str;
        fn noise(&self) -> &'static str;
     
        // A trait can provide default method definitions
        fn talk(&self) {
            // These definitions can access other methods declared in the same
            // trait
            println!("{} says {}", self.name(), self.noise());
        }
    }
     
    struct Dog { name: &'static str }
     
    impl Dog {
        fn wag_tail(&self) {
            println!("{} wags tail", self.name);
        }
    }
     
    // Implement the `Animal` trait for `Dog`
    impl Animal for Dog {
        // Replace `Self` with the implementor type: `Dog`
        fn new(name: &'static str) -> Dog {
            Dog { name: name }
        }
     
        fn name(&self) -> &'static str {
            self.name
        }
     
        fn noise(&self) -> &'static str {
            "woof!"
        }
     
        // Default trait methods can be overridden
        fn talk(&self) {
            // Traits methods can access the implementor methods
            self.wag_tail();
     
            println!("{} says {}", self.name, self.noise());
        }
    }
     
    struct Sheep { naked: bool, name: &'static str }
     
    impl Sheep {
        fn is_naked(&self) -> bool {
            self.naked
        }
     
        fn shear(&mut self) {
            if self.is_naked() {
                // Implementor methods can use the implementor's trait methods
                println!("{} is already naked!", self.name());
            } else {
                println!("{} gets a haircut", self.name);
     
                self.talk();
                self.naked = true;
            }
        }
    }
     
    impl Animal for Sheep {
        fn new(name: &'static str) -> Sheep {
            Sheep { name: name, naked: false }
        }
     
        fn name(&self) -> &'static str {
            self.name
        }
     
        fn noise(&self) -> &'static str {
            if self.is_naked() {
                "baaah"
            } else {
                "baaaaaaaaaaaah"
            }
        }
    }
     
    fn main() {
        // Type annotation is necessary in this case
        let mut dolly: Sheep = Animal::new("Dolly");
        let spike: Dog = Animal::new("Spike");
        // TODO ^ Try removing the type annotations
     
        dolly.shear();
     
        spike.talk();
        dolly.talk();
    }

Автор: applegame 04.06.15, 06:42
то есть унаследовать данные в Rust можно только добавив сеттеры/геттеры?
И какое это дает преимущество? Ведь те же яйца, вид сбоку.

Автор: DarkEld3r 04.06.15, 09:04
Цитата D_KEY @
из минусов - сложные описания в случае "классических" ОО-иерархий и полное отсутствие механизма наследования реализаций.

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

Автор: applegame 04.06.15, 09:25
DarkEld3r, это не моя цитата :)

Автор: DarkEld3r 04.06.15, 09:35
Не знаю как так получилось. Вроде, жал на "быстрая цитата". :) Пофиксил.

Автор: D_KEY 04.06.15, 21:36
Цитата DarkEld3r @
Насколько я знаю, различные варианты реализации наследования обсуждаются.

Не знаю, насколько это уместно в схеме с этими тремя механизмами... Мне кажется, что вполне достаточно какого-то механизма для делегирования реализации trait полю с возможностью переопределения методов.

Автор: DarkEld3r 04.06.15, 22:01
Цитата D_KEY @
Мне кажется, что вполне достаточно какого-то механизма для делегирования реализации trait полю с возможностью переопределения методов.

Честно говоря, не вижу принципиальной разницы. Разве что вариант с "делегированием" позволит более естественно реализовывать "множественноe наследование", которое иначе могут и запретить. Ну и ведь за делегированием будет скрываться всё то же наследование только не данных и реализации одновременно, а только реализации.

P.S. Что-то не получается холивара. :D

Автор: DarkEld3r 16.06.15, 16:02
Подброшу дровишек: при "реализации ООП через трейты" не возникает проблемы как в соседнем топике (что от чего наследовать квадрат от прямоугольника или прямоугольник от квадрата). :D

Автор: applegame 16.06.15, 20:20
Цитата DarkEld3r @
Подброшу дровишек: при "реализации ООП через трейты" не возникает проблемы как в соседнем топике (что от чего наследовать квадрат от прямоугольника или прямоугольник от квадрата). :D

Можно простецкий пример решения этой псевдопроблемы на Rust?

Автор: DarkEld3r 16.06.15, 21:50
Цитата applegame @
Можно простецкий пример решения этой псевдопроблемы на Rust?

"Проблема" ведь в том, что если мы наследуем квадрат от прямоугольника, то получим лишний бесполезный член данных. В расте нет наследования данных - нет проблемы. :D

Можно и кодом, хотя это, вряд ли, будет интересно:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    trait Rectangle {
        fn area(&self) -> i32;
    }
     
    struct RectangleData {
        a: i32,
        b: i32,
    }
     
    impl Rectangle for RectangleData {
        fn area(&self) -> i32 {
            self.a * self.b
        }
    }
     
    trait Square : Rectangle {
        // Методы уникальные для квадрата
    }
     
    struct SquareData {
        a: i32,
    }
     
    impl Rectangle for SquareData {
        fn area(&self) -> i32 {
            self.a * self.a
        }
    }
     
    impl Square for SquareData {
        // ...
    }

Автор: applegame 17.06.15, 10:18
Цитата DarkEld3r @
"Проблема" ведь в том, что если мы наследуем квадрат от прямоугольника, то получим лишний бесполезный член данных. В расте нет наследования данных - нет проблемы. :D
Никто не мешает НЕ наследовать данные и в C++. Аналог твоего растовского кода на плюсах:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    struct Rectangle {
      virtual int area() = 0;
    };
     
    struct RectangleImpl : public Rectangle {
        int a, b;
        int area() { return a * b; }
    };
     
    struct Square : public Rectangle {
    };
     
    struct SquareImpl : public Square {
        int a;
        int area() { return a * a; }
    };
     
    ...
     
    Square* square = new SquareImpl();
    Rectangle* rectangle = new RectangleImpl();


P.S. ваш Rust таки уродлив, даже плюсы симпатичней.

Автор: AVA12 17.06.15, 11:25
Вообще, деление на наследуемые классы, реализуемые интерфейсы и добавляемые примеси, по-моему, искусственное и надуманное. Вполне достаточно реализуемых классов с перекрываемыми методами. Плюс нужен механизм для разрешения конфликтов имен, например, возможность реализации классов в качестве "компонента" с возможностью доступа к неприватным свойствам и "компонентам" "хозяина". Плюс аналог паскалевского оператора AS. Этого вполне достаточно для реализации самых извращенных схем наследования. Или нет?

Автор: DarkEld3r 17.06.15, 12:57
Цитата applegame @
Никто не мешает НЕ наследовать данные и в C++.

Верно, но никто и не запрещает. Хорошо это или нет - спорный вопрос (лично я считаю, что хорошо в рамках С++), тем не менее, если требовать всегда "выделять интерфейс", то проблемы наверняка не будет. Ну а если наследование реализуют так как D_KEY предлагал, то и кода станет меньше и минусов, вроде, не будет.

Цитата applegame @
P.S. ваш Rust таки уродлив, даже плюсы симпатичней.

Аргументы будут?

Автор: applegame 17.06.15, 13:04
Цитата DarkEld3r @
Аргументы будут?
Нет, это мое субъективное оценочное мнение, которое можно проигнорировать. Не нравится мне синтксис Rust'а и все.

Автор: DarkEld3r 17.06.15, 13:06
Цитата AVA12 @
Вообще, деление на наследуемые классы, реализуемые интерфейсы и добавляемые примеси, по-моему, искусственное и надуманное.

Если я правильно понял мысль, то идея в следующем: "добавляемые примеси" (трейты), в любом случае, должны определяться отдельно так как они не привязаны к типу - можно определять для любого подходящего. Реализация тоже не может содержаться внутри типа - так как она может добавляться к существующим типам.

"Просто методы", наверное, могли бы определяться "внутри типа", чтобы было похоже на "обычное ООП", но во первых, методы тоже можно добавлять. Во вторых, так механизм выглядит единообразно с трейтами.

Цитата AVA12 @
Вполне достаточно реализуемых классов с перекрываемыми методами. Плюс нужен механизм для разрешения конфликтов имен, например, возможность реализации классов в качестве "компонента" с возможностью доступа к неприватным свойствам и "компонентам" "хозяина". Плюс аналог паскалевского оператора AS.

Тут я не понял, можно развернуть мысль? Что именно не нравится и как сделать лучше.

Автор: DarkEld3r 17.06.15, 13:14
Цитата applegame @
Не нравится мне синтксис Rust'а и все.

Жаль, в том смысле, что можно было бы попытаться обсудить те или иные решение, которые приняли разработчики языка. Как по мне, так всё там более-менее нормально. Да, местами код выглядит более громоздко из-за лайфтаймов, но это логично: добавили новую сущность - надо её как-то выражать.

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

Автор: D_KEY 17.06.15, 19:03
Цитата applegame @
Цитата DarkEld3r @
Аргументы будут?
Нет, это мое субъективное оценочное мнение, которое можно проигнорировать. Не нравится мне синтксис Rust'а и все.

Это мнение, кстати говоря, довольно часто встречается... Так что ты не одинок.

Автор: DarkEld3r 17.06.15, 20:47
Цитата D_KEY @
Это мнение, кстати говоря, довольно часто встречается..

Осталось найти язык, который не ругают (за синтаксис).

Автор: Qraizer 17.06.15, 21:17
Ассемблер?

Автор: D_KEY 17.06.15, 21:32
Языков ассемблера больше одного :)

Добавлено
Цитата DarkEld3r @
Цитата D_KEY @
Это мнение, кстати говоря, довольно часто встречается..

Осталось найти язык, который не ругают (за синтаксис).

Степень неприязни разная.

Автор: DarkEld3r 18.06.15, 09:52
Цитата D_KEY @
Степень неприязни разная.

Возможно, только она не особо и коррелирует с популярностью языка, имхо. Опять же, очень часто это "синдром утёнка". Да и мне кажется, что многие ругающие предмет неприязни видели только "на картинках", не уверен, что это о чём-то говорит.

Скажем, С++ не ругал только ленивый и не сказать, что так уж зря. Тем не менее, язык довольно популярен, ну и лично мне ощутимых неудобств синтаксис не доставляет. Немало найдётся и тех, кто будет его защищать.
Или лисп вспомнить можно - большинство сразу начнёт кричать про обилие скобок, но если хоть немного писать что-то, то как-то и не мешает вовсе.

Автор: Serafim 01.07.15, 08:16
Хм, а чем так сильно отличается поле с функцией первого класса в качестве значения поля от обычного метода, что их так надо разделять? Какой-то бред придумали в Rust по-моему с этим.

Автор: DarkEld3r 01.07.15, 08:49
Цитата Serafim @
Хм, а чем так сильно отличается поле с функцией первого класса в качестве значения поля от обычного метода, что их так надо разделять?

В расте методы (ссылка на таблицу) хранятся не в объекте, а в трейте. Собственно, в С++ тоже есть разница между методом и полем содержащим функцию.

Цитата Serafim @
Какой-то бред придумали в Rust по-моему с этим.

Выше я приводил своё понимание того почему сделали именно так. Предложи вариант получше, с учётом особенностей языка.

Автор: D_KEY 01.07.15, 08:50
Цитата Serafim @
Хм, а чем так сильно отличается поле с функцией первого класса в качестве значения поля от обычного метода, что их так надо разделять?

В классовом ООП нет никакой надобности хранить методы в экземпляре. Грубо говоря.

Автор: Serafim 01.07.15, 08:57
Я не про частности, я про общую теорию. Имхо, вообще разделения на метод\поле не должно быть на уровне языка никакого, лишь может быть на уровне визуального представления. Тоже и касается трейтов (если не вдаваться в тонкости раста) - это сокращение для абстрактного класса. Короче всё это в целом - просто попытка реализовать уже существующее, превратив привычные `class extends` в `impl for`.

Добавлено
Короче я за классы и трейты (но не те трейты, которые в расте, а нормальные).

Добавлено
те же структуры (struct) вообще ничем не отличаются от обычных ассоциативных массивов (ака хешей)...

Автор: DarkEld3r 01.07.15, 09:09
Цитата Serafim @
Имхо, вообще разделения на метод\поле не должно быть на уровне языка никакого, лишь может быть на уровне визуального представления

Но D_KEY всё правильно сказал - что в С++, что в расте "просто методы" не хранятся в объекте. Какой смысл хранить их в каждом объекте?

Даже если говорить про "виртуальные функции", то всё равно есть разница - один указатель против одного указателя на каждый метод.

Цитата Serafim @
(если не вдаваться в тонкости раста) - это сокращение для абстрактного класса

Ну а если вдаваться, то нет. Трейты нужны, в первую очередь, для дженериков, которые разворачиваются на этапе компиляции, как и плюсовые шаблоны, но накладывают более явные ограничения на типы.

Цитата Serafim @
те же структуры (struct) вообще ничем не отличаются от обычных ассоциативных массивов (ака хешей)...

Видимо, я чего-то не понимаю. О каком языке речь?

Автор: Serafim 01.07.15, 09:22
Цитата DarkEld3r @
Но D_KEY всё правильно сказал - что в С++, что в расте "просто методы" не хранятся в объекте. Какой смысл хранить их в каждом объекте?

Как оно хранится на уровне рантайма - это уже не важно и частности. Можно хранить в прототипе (см JS, etc...), никто не мешает.

Цитата DarkEld3r @
Ну а если вдаваться, то нет. Трейты нужны, в первую очередь, для дженериков, которые разворачиваются на этапе компиляции, как и плюсовые шаблоны, но накладывают более явные ограничения на типы.

Эм. А чем "T implements SomeInterface" не угодил? Я хз, есть ли такое в сях. :-?

Цитата DarkEld3r @
Видимо, я чего-то не понимаю. О каком языке речь?

Ну о любом наверное, где есть хеши или мапы. Во внутреннем представлении - это всё же адрес с именем и массив из произвольных кей-велью значений. Ничего не мешает написать точно такой же генератор таких структур на любом классовом языке с достаточным уровнем мета-магии, чтоб перехватывать `method_missing`.



Я всё это веду к тому, что модель Rust по-моему - это шаг назад. От абстракции к реализации. Вон, сколько пытались в Objective-C от этих разделений избавиться? Избавились - сделали Swift. А тут какой-то Objective-C, только в профиль =)

Добавлено
Цитата Serafim @
Эм. А чем "T implements SomeInterface" не угодил? Я хз, есть ли такое в сях.

Ну что-то как-то так имеется ввиду:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    interface Some<T implements IAny> {
      public function test(T asdasd);
    }


Добавлено
Если именно это подразумевалось под "строгостью" дженериков

Автор: DarkEld3r 01.07.15, 09:43
Цитата Serafim @
Как оно хранится на уровне рантайма - это уже не важно и частности.

Во первых, для низкоуровневых языков (а раст претендует именно на эту нишу) оно очень даже важно оказывается. Во вторых, изначально вопрос был "чем отличается" - так вот вполне отличается.

Цитата Serafim @
Эм. А чем "T implements SomeInterface" не угодил? Я хз, есть ли такое в сях. :-?

В сях нет, тем и не угодил.
Но тогда я не понимаю претензию к трейтам. Значит интерфейсы, как отдельная сущность не смущают, а трейты уже не нравятся? А разница-то в чём?

Цитата Serafim @
Ну о любом наверное, где есть хеши или мапы.

Но это не так. Ещё раз - методы статически вызываются и нигде не хранятся. "Абстрагирование" от этого мне нафиг не нужно, если оно потянет за собой необходимость каждый метод в рантайме искать в хеш-таблицах. Конечно, "method_missing" можно сделать и для "нормальных методов" и в D так и сделано, но в расте такого нет.

Цитата Serafim @
Ну что-то как-то так имеется ввиду:

Наверное, хотя тут я вижу только новый интерфейс. В расте "быть дженериками" (иметь ограничение по типу) могут функции, структуры, енумы и трейты.

Цитата Serafim @
А тут какой-то Objective-C, только в профиль =)

Objective-C знаю не очень хорошо, но похожего вижу крайне мало, ну кроме (немного) "С-подобного синтаксиса". Тут всё не вертится вокруг "посылки сообщений", отсутствующий метод обработать нельзя и т.д. С удовольствием послушаю какие тут можно увидеть сходства. И почему именно с Objective-C, а не С или С++.

Автор: D_KEY 01.07.15, 09:44
Цитата Serafim @
Я не про частности, я про общую теорию

Это про какую такую теорию? :) По ООП у нас какая теория-то? Буч? Мейер? Так там тоже классовое ООП и метод относится не к экземпляру, а к классу. Т.е. какой метод у объекта однозначно определяется классом, а какое значение будет у полей - экземпляром. Это считай и есть состояние объекта. Методы к этому состоянию никакого отношения не имеют.

Цитата
Имхо, вообще разделения на метод\поле не должно быть на уровне языка никакого

Смотря что ты имеешь в виду. Для клиента класса - да, желательно, чтобы при изменении стратегии хранения(храним поле или же вычисляем значение в методе) клиент не нужно было менять. Есть "принцип универсального доступа" и пр. такое. Но это скорее о синтаксисе. В "классических" языках решается или тем, что все публичное оформляется в виде методов(в простейшем случае get/set), или пропертями всяческими.

Цитата
но не те трейты, которые в расте, а нормальные

В чем отличие?

Цитата
те же структуры (struct) вообще ничем не отличаются от обычных ассоциативных массивов (ака хешей)...

:facepalm:

Автор: DarkEld3r 01.07.15, 09:46
Цитата Serafim @
От абстракции к реализации.

Ну и этого понять не могу. Что тут подразумевает? Где абстракция/реализация? Как методы определять/хранить? По моему, это фигня, а не "абстракция".

Ну и я всё-таки повторюсь: в расте методы типу могут добавляться извне. Можно реализовывать трейты объявленные в сторонней библиотеке для своих типов и наоборот. Именно поэтому оно и выносится отдельно. Как можно иначе, если мы хотим сохранить эту особенность я плохо представляю.

Автор: D_KEY 01.07.15, 09:48
Цитата Serafim @
Ну о любом наверное, где есть хеши или мапы. Во внутреннем представлении - это всё же адрес с именем и массив из произвольных кей-велью значений.

Хеши и мапы(ты имел в виду, наверное, мапы на деревьях) - это структуры данных. А структуры в языках - это способ описания группы полей, которые в памяти будут просто следовать друг за другом(условно), а имена полей будут доступны только на этапе компиляции.

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

Добавлено
Цитата Serafim @
А тут какой-то Objective-C, только в профиль =)

А что общего? В Objective-C методы как раз в словариках ищутся.

Автор: Qraizer 01.07.15, 10:11
DarkEld3r, видимо, имел в виду, что ассоциативный массив с константным набором ключей и структура неотличимы.

Автор: DarkEld3r 01.07.15, 10:33
Цитата Qraizer @
видимо, имел в виду, что ассоциативный массив с константным набором ключей и структура неотличимы.

Видимо, я не могу настолько "абстрагироваться от реализации". :) Если у нас константный набор ключей и они известны на этапе компиляции, то пусть их компилятор и подставляет, что он собственно и делает для методов.

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

Выделяется тут разве что D, с его возможность обработать нереализованные методы.

Автор: D_KEY 01.07.15, 10:57
Цитата Qraizer @
DarkEld3r, видимо, имел в виду, что ассоциативный массив с константным набором ключей и структура неотличимы.

Неотличимы в каком смысле? В смысле интерфейса они могут как отличаться, так и нет. В реализации они отличаются, хотя в динамическом языке "структура" по сути представлена в виде словаря, как правило, да.

Автор: Serafim 01.07.15, 11:10
Про ObjC поясню - имелась ввиду схожесть, когда куски одной сущности раскидываются по разным файлам. Хотя да, как-то забыл про хедеры, которые могут тоже разделять единые сущности.

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

Короче да, наверное стоит всё же поподробнее разобраться в чём у них там основной замут. Хотя если настаиваете - могу дальше выдвигать псевдотеории на тему "классы рулят". :whistle:

Добавлено
Цитата Qraizer @
DarkEld3r, видимо, имел в виду, что ассоциативный массив с константным набором ключей и структура неотличимы.

:yes: +1

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

Автор: DarkEld3r 01.07.15, 11:13
Цитата Serafim @
С другой стороны в пыхе есть и интерфейсы, и трейты, и поведение у них совершенно разное...

Если не лень и можно объяснить в двух словах, то я бы с удовольствием послушал.

Автор: Serafim 01.07.15, 11:15
С другой стороны не всё так просто будет при сборке, в байткод JVM, там вообще желательно привязывать все сущности к реально существующим в Java. Но это уже оффтоп. Просто небольшое исключение из правил (растовая структура == хеш).

Добавлено
Цитата DarkEld3r @
Если не лень и можно объяснить в двух словах, то я бы с удовольствием послушал.

Интерфейс - тупо интерфейс, как мы привыкли, добавляет толику полиморфизма и надёжности кода. Ну и плюс (по-моему огромный) - могут содержать только методы и константы, благодаря чему достигается лаконичность и однозначность оных интерфейсов. Для остального есть мастеркард абстрактные классы, там даже можно реализацию кусками впиливать.

Трейты же - это набор некой логики, которые можно использовать в качестве источников методов\полей и резолвить их налету. Т.е. тупо примеси:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    trait A {
        public function some(){}
    }
     
    tarit B {
        public function some(){}
    }
     
     
    class Main {
      // Через use подрубаем в класс, но т.к. имена конфликтуют, нужно воспользоваться резолвом
      use A, B {
        B::some insteadof A; // Ставим метод some из B "выше", нежели А. Т.е. перекрываем, так сказать один другим.
        B::some as any; // Если нужно оба, то переименовываем метод some в any, внутри класса Main
      }
    }


Это не классы, т.к. их нельзя инстанциировать, просто некая логика. Например классический пример с "лучником" и "мечником", где показано превосходство композиции над наследованием можно реализовать через трейты, хотя по идеологии это не совсем так будет, думаю. Можно ещё как некие классовые аннотации использовать (они резолвятся через рефлексию нормально) ну или просто хелперы. Например:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    trait StringifyObject
    {
        public function toString()
        {
            return '<Object#' . static::class . '>';
        }
    }
     
    class Some {
      use StringifyObject;
    }
     
    echo (new Some)->toString();

Автор: applegame 01.07.15, 21:08
Цитата D_KEY @
В чем отличие?
Интерфейс - это тип задающий набор методов, которые необходимо реализовать, а трейт - это просто набор условий.
Условиями может быть наличие каких-то методов подобно интерфейсу, а может быть возможность неявно кастоваться к заданному типу, или иметь возможность быть вызванным как функция с двумя любыми параметрами, или быть целым числом, или уметь сериализоваться в строку библиотекой СуперСериализатор. Все что душе угодно и позволяет язык. Разницу чувствуешь?
Для того чтобы унаследоваться от интерфейса, объект должен быть классом и "знать" об этом интерфейсе, а вот трейту может соответствовать объект любого типа: скаляр, массив, хэш, структура, класс, что угодно. И этот объект не обязан ничего "знать" об этом трейте. Это полезно.

Например в D есть трейт isInputRange. Любой объект для которого определены примитивы - empty, popFront, и front будут удовлетворять этому трейту. Мне не нужно наследоваться или как-то еще привязываться к isInputRange. Мне достаточно просто описать эти методы. Любой объект O, для которого компилируются O.empty, O.popFront и O.front будет считаться InputRange.

Можно взять произвольный написанный левым дядей объект и самому дописать функции empty(O), popFront(O) и front(O) и благодаря UFCS объект ничего не знающий о трейте isInputRange внезапно начинает ему соответствовать.

Верно и обратное: можно создавать трейты, для уже определенных типов, например, в какой-нибудь сторонней библиотеке.

В D полно трейтов из серии isArray, isNumeric, isMutable, isCallable и так далее.

Возможны ли такие фокусы в Rust?

Автор: korvin 01.07.15, 21:57
Цитата applegame @
Интерфейс - это тип задающий набор методов, которые необходимо реализовать, а трейт - это просто набор условий.
Условиями может быть наличие каких-то методов подобно интерфейсу, а может быть возможность неявно кастоваться к заданному типу, или иметь возможность быть вызванным как функция с двумя любыми параметрами, или быть целым числом, или уметь сериализоваться в строку библиотекой СуперСериализатор. Все что душе угодно и позволяет язык. Разницу чувствуешь?
Для того чтобы унаследоваться от интерфейса, объект должен быть классом и "знать" об этом интерфейсе, а вот трейту может соответствовать объект любого типа: скаляр, массив, хэш, структура, класс, что угодно. И этот объект не обязан ничего "знать" об этом трейте. Это полезно.

Например в D есть трейт isInputRange. Любой объект для которого определены примитивы - empty, popFront, и front будут удовлетворять этому трейту. Мне не нужно наследоваться или как-то еще привязываться к isInputRange. Мне достаточно просто описать эти методы. Любой объект O, для которого компилируются O.empty, O.popFront и O.front будет считаться InputRange.

А зачем, при наличии таких гибких и удобных трейтов, иметь ещё и интерфейсы?

Автор: D_KEY 01.07.15, 22:25
applegame, мой вопрос был об отличии trait'ов в Rust от неких "нормальных" trait'ов.

Добавлено
Цитата applegame @
Интерфейс - это тип задающий набор методов, которые необходимо реализовать, а трейт - это просто набор условий.

Это зависит от языка.

Автор: applegame 02.07.15, 06:33
Цитата korvin @
А зачем, при наличии таких гибких и удобных трейтов, иметь ещё и интерфейсы?
ИМХО, интерфейсы это в первую очередь динамический полиморфизм, а трейты скорее статический. И я считаю, что смешивать их в одну кучу - не очень хорошая идея. По крайней мере в C/C++ подобных языках.
Цитата D_KEY @
Это зависит от языка.
Зависит.В С++ и D, насколько я понимаю, трейты как раз то, что я описал.

Автор: D_KEY 02.07.15, 07:00
Цитата applegame @
Зависит.В С++ и D, насколько я понимаю, трейты как раз то, что я описал.

В C++ нет ни интерфейсов, ни трейтов :) А в D вроде так, да. И ты разбираешься в D намного лучше меня, так что тут спорить не стал бы.

Автор: DarkEld3r 02.07.15, 08:27
По моему, все стали рассказывать о том, что такое трейт в их любимом языке. :)

Цитата D_KEY @
В C++ нет ни интерфейсов, ни трейтов

А как же "type_traits"? :)
Интерфейсы, по факту, тоже есть. Не обязательно ведь наличие именно ключевого слова?

Цитата applegame @
Возможны ли такие фокусы в Rust?

Нет. Растовые трейты - совсем другое, можно сказать, что они "менее гибкие", можно, что "более строгие". Их надо реализовывать для каждого типа отдельно, как бонус - в результате с такими типами могут работать не только "шаблонные" функции.

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

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

Автор: MyNameIsIgor 02.07.15, 08:33
Цитата DarkEld3r @
Интерфейсы, по факту, тоже есть. Не обязательно ведь наличие именно ключевого слова?

А что, по-вашему, является интерфейсом в C++? :)
Правильный ответ
Концепты

Автор: DarkEld3r 02.07.15, 08:45
Цитата MyNameIsIgor @
А что, по-вашему, является интерфейсом в C++? :)

Да то же, что и в D - абстрактные классы.

Цитата MyNameIsIgor @
Концепты

А как же "интерфейсы это в первую очередь динамический полиморфизм"?

Автор: D_KEY 02.07.15, 09:20
Цитата DarkEld3r @
А как же "type_traits"? :)
Интерфейсы, по факту, тоже есть. Не обязательно ведь наличие именно ключевого слова?

Так в том и дело, что "интерфейсы" и "трейты" зависят от языка.

Автор: MyNameIsIgor 02.07.15, 09:20
Цитата DarkEld3r @
А как же "интерфейсы это в первую очередь динамический полиморфизм"?

А при чём тут я?

Автор: D_KEY 02.07.15, 09:21
Цитата DarkEld3r @
Изначально я тоже думал, что шаблоны плюсовые намного гибче/удобнее. Сейчас уже не настолько уверен.

Если добавить концепты, то разве останутся сомнения?

Автор: MyNameIsIgor 02.07.15, 09:33
Цитата D_KEY @
Цитата DarkEld3r @
Изначально я тоже думал, что шаблоны плюсовые намного гибче/удобнее. Сейчас уже не настолько уверен.

Если добавить концепты, то разве останутся сомнения?

Концепты не добавят функциональности.
И я вообще не понимаю как сравниваются плюсовые шаблоны и растовые трэйты...

Автор: DarkEld3r 02.07.15, 09:35
Цитата MyNameIsIgor @
А при чём тут я?

Действительно не при чём, ошибся. Но с этим мнением я согласен. Можем поспорить. :)

Цитата D_KEY @
Если добавить концепты, то разве останутся сомнения?

Ну... есть всё-таки разные нюансы. В расте нужного поведения можно добиться через макросы. А плюсовые шаблоны местами корявы. Впрочем, макросы в расте тоже не сказать чтобы сильно "дружелюбны".

Добавлено
Цитата MyNameIsIgor @
И я вообще не понимаю как сравниваются плюсовые шаблоны и растовые трэйты...

Растовые дженерики. В них нельзя, как в С++, делать с аргументами что угодно с проверкой после подстановки параметров. "Границы" типов жестко задаются как раз трейтами. То есть, если для каждого параметра прописывать ограничения при помощи концептов, то получится нечто похожее.

Автор: MyNameIsIgor 02.07.15, 09:38
Цитата DarkEld3r @
Можем поспорить

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

Добавлено
Цитата DarkEld3r @
Растовые дженерики

Вот я и говорю - как вы так сравнили шаблоны и трейты? Я не понял. По каким параметрам?
Цитата DarkEld3r @
В них нельзя, как в С++, делать с аргументами что угодно с проверкой после подстановки параметров. "Границы" типов жестко задаются как раз трейтами.

И это fail. Ибо, как я понимаю, нельзя соорудить что-то типа плюсовых контейнеров, когда, например, требование наличия конструктора по-умолчанию зависит от метода, а не контейнера вообще.

Автор: DarkEld3r 02.07.15, 10:01
Цитата MyNameIsIgor @
Вы говорите об интерфейсах исключительно в их понимании в ООП.

Тогда почему я ошибаюсь? "Границы"-то мы не оговаривали. А уж контекст был про трейты/интерфейсы в D.

Цитата MyNameIsIgor @
Вот я и говорю - как вы так сравнили шаблоны и трейты? Я не понял. По каким параметрам?

Я надеюсь тут опять не казуистика? Тогда сравнили напрямую - по их предназначению. А именно написанию обобщённого кода. И ещё раз: не шаблоны и трейты, а шаблоны, которые могут (в перспективе) быть ограничены концептами и дженерики, которые обязательно ограничены трейтами.

Цитата MyNameIsIgor @
И это fail.

Нет.

Цитата MyNameIsIgor @
Ибо, как я понимаю, нельзя соорудить что-то типа плюсовых контейнеров, когда, например, требование наличия конструктора по-умолчанию зависит от метода, а не контейнера вообще.

Почему это нельзя?

Автор: MyNameIsIgor 02.07.15, 10:16
Цитата DarkEld3r @
Тогда почему я ошибаюсь?

Потому что мы говорим об интерфейсах в языке. В мультипарадигменном языке C++. А не в одной из школ ООП.
По-вашему получается, что встроенные типы интерфейса не имеют. Я же вижу std::function - вполне себе использование интерфейса в классическом понимании.
Цитата DarkEld3r @
"Границы"-то мы не оговаривали.

Как же это?
Цитата DarkEld3r @
Цитата MyNameIsIgor @
А что, по-вашему, является интерфейсом в C++? :)

Да то же, что и в D - абстрактные классы.

Цитата DarkEld3r @
Почему это нельзя?

Так я понял ваше объяснение. Я вас не так понял? Вы утверждаете, что в Rust можно соорудить аналог std::map, у которого будет аналог operator[], создающий объект конструктором по-умолчанию при его отсутствии в словаре, и при этом этот аналог можно будет инстанцировать типом без конструктора по умолчанию?

Автор: D_KEY 02.07.15, 10:28
Цитата MyNameIsIgor @
Концепты не добавят функциональности.

Ну...

Цитата
И я вообще не понимаю как сравниваются плюсовые шаблоны и растовые трэйты...

Я так понимаю, что имеются в виду дженерики + трейты.

Добавлено
Цитата MyNameIsIgor @
Ибо, как я понимаю, нельзя соорудить что-то типа плюсовых контейнеров, когда, например, требование наличия конструктора по-умолчанию зависит от метода, а не контейнера вообще.

Ну в трейте для элемента у контейнера не будет этого требования, а в требованиях метода - будет. По-моему так.

Автор: MyNameIsIgor 02.07.15, 10:37
Цитата D_KEY @
Ну в трейте для элемента не будет этого требования, а в требованиях метода - будет. По-моему так.

Подобные примеры мне пока не попадались.

Автор: applegame 02.07.15, 10:49
Цитата DarkEld3r @
Да то же, что и в D - абстрактные классы.
Не совсем. В D есть ключеве слово interface. Абстрактные классы тоже есть, но они отличаются. Это не суть.

В общем смысле концепты несомненно являются интерфейсами. Но разве ООП-интерфейсы жабы или D не являются интерфейсами в общем смысле? И то и другое является подмножеством интерфейсов в общем смысле. Говоря о динамическом полиморфизме я имел ввиду интерфейсы в узком смысле, а именно ООП-ные интерфейсы.

Основное отличие ООП-ных интерфейсов и концептов как раз в статичности/динамичности. Опять же берем в качестве примера D (можно считать что это некий вариант C++ с концептами):
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    auto sort(R)(R range) if(isRandomAccessRange!R) {
       ...
    }

Функция sort принимает любой тип удовлетворяющий концепту (он же является и trait-ом) isRandomAccessRange. Но такие "интерфейсы" могут принимать в качестве параметра только шаблонные функции. Статический полиморфизм. Как нам создать вектор произвольных объектов с интерфейсом isRandomAccessRange? Только написав некий враппер с применением, внезапно, ООП-ных интерфейсов.

Поэтому разделение трейтов/концептов и ООП-интерфейсов на две разные, но взаимодополняющие сущности мне представляется вполне разумным. В Rust, насколько я понял, попытались как-то объединить в единую сущность. Насколько это можно удачно реализовать в языках со статической типизацией трудно сказать. Главный вопрос: зачем? ООП-интерфейсы + концепты отлично себя показали, к чему изобретать велосипед?

Автор: D_KEY 02.07.15, 10:53
Цитата applegame @
В Rust, насколько я понял, попытались как-то объединить в единую сущность. Насколько это можно удачно реализовать в языках со статической типизацией трудно сказать. Главный вопрос: зачем?

Затем, что не надо "один и тот же" интерфейс(в широком смысле слова) описывать дважды. И оберток никаких не надо.
Кстати, в одном давнем холиваре затрагивалась эта тема, я там как раз говорил о таких интерфейсах. Только я хотел их утиности еще. Если правильно помню :D

Автор: applegame 02.07.15, 11:01
Цитата D_KEY @
Затем, что не надо "один и тот же" интерфейс(в широком смысле слова) описывать дважды. И оберток никаких не надо.
Хм. А как описать в Rust интерфейс/трейт isMutable или допустим isImplicitlyConvertible. Думаю из названия понятно что это за интерфейсы. Возможно я ошибаюсь, но не являются ли растовские трейты по сути теми же ООП-ными интерфейсами, вид сбоку?

Автор: D_KEY 02.07.15, 11:03
Цитата applegame @
Хм. А как описать в Rust интерфейс/трейт isMutable или допустим isImplicitlyConvertible.

Давай начнем с задачи, а то ты уже про реализацию спрашиваешь. Что нужно-то?

Добавлено
И как их описать в D?

Автор: MyNameIsIgor 02.07.15, 11:05
Цитата applegame @
В общем смысле концепты несомненно являются интерфейсами. Но разве ООП-интерфейсы жабы или D не являются интерфейсами в общем смысле? И то и другое является подмножеством интерфейсов в общем смысле.

Концепты (по крайней мере в их текущем виде словесных требований) и есть "интерфейсы в общем смысле" - набор операций с типом.
Разговор же о более узкой области просто не имеет смысла по крайней мере применительно к C++, ибо оставляет за бортом очень много типов.
Цитата applegame @
Основное отличие ООП-ных интерфейсов и концептов как раз в статичности/динамичности

Нет. Динамическая диспетчеризация просто вытекает из ООП. Поэтому главное отличие - говорим мы только об ООП или же вообще.
Цитата applegame @
Функция sort принимает любой тип удовлетворяющий концепту (он же является и trait-ом) isRandomAccessRange. Но такие "интерфейсы" могут принимать в качестве параметра только шаблонные функции. Статический полиморфизм.

Это зависит от языка. Применительно к C++ - да. Затирать тип придётся вручную. Но вот есть C#. Там помимо ООП-интерфейсов есть ограничения на параметры обобщений, которые как и сами по себе интерфейсы в некотором смысле, так и могут требовать реализации ООП-интерфейсов. Так вот там это будет динамический полиморфизм, компилятор не станет инстанцировать обобщение для каждого типа, а затрёт тип. Т.е. наблюдается противоположная C++ ситуация.
Потому вполне себе можно вообразить язык, где компилятор способен делать и то, и то. И такая функция будет как статически, так и динамически полиморфна.

Вывод: интерфейсы ортогональны способу диспетчеризации.
Цитата applegame @
Только написав некий враппер с применением, внезапно, ООП-ных интерфейсов.

ООП-интерфейсы не единственный способ (по крайней мере в плюсах) обеспечения динамической диспетчеризации.

Добавлено
Цитата applegame @
Возможно я ошибаюсь, но не являются ли растовские трейты по сути теми же ООП-ными интерфейсами, вид сбоку?

Именно ими они и являются.

Автор: applegame 02.07.15, 11:09
Функция принимающая любой тип, который неявно кастуется в строку:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void foo(T)(T param) if(isIplicitlyConvertible!(T, string)) {
        ...
        string bar = param;
        ...
    }

Ну и вторая задача: без оберток создать массив в который можно было бы засунуть объект любого типа неявно кастующегося в строку.

В D, кстати, вторая задача неразрешима, в C++ насколько я понимаю тоже. Неужели Rust настолько крут, что ему это по силам?

Автор: D_KEY 02.07.15, 11:10
Цитата applegame @
который неявно кастуется в строку

Для этого в языке должны быть неявные касты...

Автор: applegame 02.07.15, 11:11
Интересно можно ли вообще isImplicitlyConvertible считать интерфейсом в общем смысле? Или интерфейс - это нечто обязательно представленное наличием/отсутствием определенных методов, а не произвольных свойств?

Добавлено
Цитата D_KEY @
Для этого в языке должны быть неявные касты...
В Rust нельзя 8-мибитное целое неявно скастовать к 16-битному? Или неявно скастовать наследника к предку?

Автор: D_KEY 02.07.15, 11:14
В Rust есть, например:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    pub trait ToString {
        fn to_string(&self) -> String;
    }


И его можно использовать как для ограничения дженерика(в статике, с генерацией кода, как я понял), так и как обычный аргумент. Ну да, можно хранить в массиве.

Добавлено
Цитата applegame @
В Rust нельзя 8-мибитное целое неявно скастовать к 16-битному?

Ну встроенные(несужающие) касты есть какие-то, да. Но свои сделать нельзя, если я правильно понимаю.

Автор: applegame 02.07.15, 11:20
А если я сделаю объект с функцией to_string, но без привязки к трейту ToString, то упомянутый тобой дженерик примет это тип?

Автор: D_KEY 02.07.15, 11:22
Цитата applegame @
А если я сделаю объект с функцией to_string, но без привязки к трейту ToString, то упомянутый тобой дженерик примет это тип?

Нет. Я вон выше писал, что мне бы хотелось еще и утиности.

Но в Rust ты можешь добавить реализацию trait'а для типа. Тип и trait'ы же отдельно описываются.

Автор: DarkEld3r 02.07.15, 11:45
Цитата MyNameIsIgor @
Как же это?

Ну и? Это и есть контекст:
1. В D интерфейс - это...
2. В С++ интерфейсов есть.
3. Есть такие же как в D, только без ключевого слова.
И тут внезапно я о неправильных интерфейсах говорю.

Цитата MyNameIsIgor @
Вы утверждаете, что в Rust можно соорудить аналог std::map, у которого будет аналог operator[], создающий объект конструктором по-умолчанию при его отсутствии в словаре, и при этом этот аналог можно будет инстанцировать типом без конструктора по умолчанию?

А в чём проблема? Разносим интерфейс мапы по нескольким трейтам, у тех, которых надо будет - требуем к типу реализацию трейта Default. Вот пример на коленке набросал.

Добавлено
Цитата D_KEY @
Ну в трейте для элемента у контейнера не будет этого требования, а в требованиях метода - будет. По-моему так.

Почти, будут просто разные трейты.

Автор: DarkEld3r 02.07.15, 11:53
Цитата applegame @
Основное отличие ООП-ных интерфейсов и концептов как раз в статичности/динамичности.

Разве отличаются они не просто некоторыми ограничениями? Ну там отсутствие данных в интерфейсе, ограничение на множественное наследование и т.д.

Цитата applegame @
Основное отличие ООП-ных интерфейсов и концептов как раз в статичности/динамичности.

Разницу вполне понимаю и терминологический спор не я затеял. :)

Цитата applegame @
Главный вопрос: зачем? ООП-интерфейсы + концепты отлично себя показали, к чему изобретать велосипед?

Во первых, трейты в расте похожи на классы типов в хаскеле, например. Тоже ведь можно сказать, что они себя отлично показали.
Во вторых, концепты "отлично себя показали" весьма мало где. В С++ их до сих пор нет, например.

Повторюсь - не я придумывал дженерики раста и поначалу тоже был сильно недоволен. Да теряется гибкость. Насколько часто она будет нужна - другой вопрос. Зато не возникнет ситуация, что автор шаблонного кода тупо поленился ограничения (в виде концептов) прописывать. Небольшой плюс.

Автор: applegame 02.07.15, 11:53
Цитата D_KEY @
Нет. Я вон выше писал, что мне бы хотелось еще и утиности.
Тогда это фактически ООП-интерфейс. Статически проверить что объект унаследован от ООП-интерфейса как нефиг делать:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void foo(T)(T obj) if(is(T: ToString)) {
        ...
    }

Цитата D_KEY @
Но в Rust ты можешь добавить реализацию trait'а для типа. Тип и trait'ы же отдельно описываются.
Вот это уже интересней. То есть можно создать массив для типа ToString, потом взять совершенно левый объект описать для него реализацию trait'a ToString и вуаля - пихай в массив. Но постой-ка. Это сильно похоже на тот самый враппер-декоратор, который я упомянул выше, нет?
Цитата applegame @
Только написав некий враппер с применением, внезапно, ООП-ных интерфейсов.

Автор: DarkEld3r 02.07.15, 11:56
Цитата applegame @
isImplicitlyConvertible

В расте нет "неявного приведения" вообще. :)

Цитата applegame @
isMutable

А можно пример как это можно использовать? Ну и в расте, как и в С++, мутабельность на уровне объектов, а не типов. То есть, если нам хочется с обоими видами обьектов работать, то просто будет две функции - принимающая просто ссылку и мутабельную ссылку соответственно.

Автор: D_KEY 02.07.15, 11:59
Цитата applegame @
Статически проверить что объект унаследован от ООП-интерфейса как нефиг делать:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void foo(T)(T obj) if(is(T: ToString)) {
        ...
    }

Да, если не учитывать разницу между trait'ами и ОО-интерфейсами.

Цитата
Вот это уже интересней. То есть можно создать массив для типа ToString, потом взять совершенно левый объект описать для него реализацию trait'a ToString и вуаля - пихай в массив. Но постой-ка. Это сильно похоже на тот самый враппер, который я упомянул выше, нет?

Возможно. Давай на код посмотрим. Я пока не вижу разницы, кроме той, что trait'ы и impl'ы - фичи языка, а обертки пишутся руками как захочется, да и при использовании в обертку нужно заворачивать самому.

Добавлено
Цитата DarkEld3r @
Цитата applegame @
isImplicitlyConvertible

В расте нет "неявного приведения" вообще. :)

Встроенные примитивные типы вроде приводятся(без потери точности).

Автор: applegame 02.07.15, 12:06
Цитата DarkEld3r @
Во первых, трейты в расте похожи на классы типов в хаскеле, например. Тоже ведь можно сказать, что они себя отлично показали.
ИМХО, классы типов в Хаскеле скорее похожи как раз на концепты.
Цитата DarkEld3r @
Во вторых, концепты "отлично себя показали" весьма мало где. В С++ их до сих пор нет, например.
Нечто подобное сделали, хоть и через жопу - BCCL

Добавлено
Цитата D_KEY @
Встроенные примитивные типы вроде приводятся(без потери точности).
А трейты-потомки к трейтам-предкам тоже явно кастовать надо?

Автор: D_KEY 02.07.15, 12:14
Цитата applegame @
Цитата DarkEld3r @
Во первых, трейты в расте похожи на классы типов в хаскеле, например. Тоже ведь можно сказать, что они себя отлично показали.
ИМХО, классы типов в Хаскеле скорее похожи как раз на концепты.

Не, они посередине :D Т.е. требования они предъевляют к типам, но описывать могут только функции, да и в динамике представлены чем-то вроде списка указателей на функции.

Автор: Qraizer 02.07.15, 12:20
Цитата MyNameIsIgor @
А что, по-вашему, является интерфейсом в C++? :)
Правильный ответ
Концепты
Отнюдь. Концепты описывают как раз трейты. Не понимаю, о чём спор у вас. Трейты – это свойства типа, интерфейсы – это возможности типа. За разницей в терминах – в толковый словарь он-лайн.
Свойства типа в связи со строгой типизацией – не только в Плюсах, как я понимаю – объектной модели задаются в compile-time. Касты в семантике static_cast<> как раз и выполняются с целью сменить свойства объекта. Возможности же типа определяются его состоянием в первую очередь – файл может читаться, но только если он предварительно открыт – и они не обязаны оставаться неизменными в run-time. Отсюда и кажущееся деление на статичность/динимичность свойств/возможностей.
Цитата applegame @
Ну и вторая задача: без оберток создать массив в который можно было бы засунуть объект любого типа неявно кастующегося в строку.
Не понял. Задача написать метафункцию isConvertible<> на Плюсах? Я это делал, задолго до знакомства с бустом. И у Александреску есть.
Цитата DarkEld3r @
Во вторых, концепты "отлично себя показали" весьма мало где. В С++ их до сих пор нет, например.
Потому что без них, как и интерфейсов, вполне себе живётся на аналогах. Интерфейс – абстрактный класс, имеющий только чистые методы и не имеющий полей, который наследуется всегда виртуально. Трейты вообще не нуждаются в отдельном описании, любой шаблон предъявляет требования к свойствам своих аргументов посредством выполняемых над ними операций. Беда в том, что такие трейты ненаглядны и зачастую неочевидны, потому концепты в целом бы не помешали. А интерфейсы в лице отдельной сущности нафик не нужны. Они нужны там, где нет множественного наследования реализаций, чтобы не смущать компилятор.

Автор: MyNameIsIgor 02.07.15, 12:29
Цитата DarkEld3r @
Ну и? Это и есть контекст:
1. В D интерфейс - это...
2. В С++ интерфейсов есть.
3. Есть такие же как в D, только без ключевого слова.
И тут внезапно я о неправильных интерфейсах говорю.

Конечно неправильно. Потому что интерфейсы в C++ - это концепты. Что непонятного? :-?
Цитата DarkEld3r @
А в чём проблема? Разносим интерфейс мапы по нескольким трейтам, у тех, которых надо будет - требуем к типу реализацию трейта Default.

Ok, понял.

Добавлено
Цитата Qraizer @
За разницей в терминах – в толковый словарь он-лайн.

А можно ссылку на чуть более авторитетный источник? TAPL, например? Foundations for Programming Languages?

Автор: Qraizer 02.07.15, 13:03
:sarcasm: Англо-русский словарь не подойдёт?

Автор: MyNameIsIgor 02.07.15, 13:12
Цитата Qraizer @
:sarcasm: Англо-русский словарь не подойдёт?

А, понял откуда вы определения берёте. За сим закончим.

Автор: applegame 02.07.15, 13:13
Цитата MyNameIsIgor @
ООП-интерфейсы не единственный способ (по крайней мере в плюсах) обеспечения динамической диспетчеризации.
Эм. Короткий пример можно?
Цитата DarkEld3r @
Ну и в расте, как и в С++, мутабельность на уровне объектов, а не типов.
В C++ мутабельности/иммутабельности как таковой нет, но есть константность, которой иногда можно заменить иммутабельность. Константость задается вполне себе на уровне типа. И можно создать шаблонную функцию принимающую как константный, так и мутабельный тип.

Автор: MyNameIsIgor 02.07.15, 13:14
Цитата applegame @
Короткий пример можно?

Указатели на функции же.

Автор: applegame 02.07.15, 13:18
Цитата MyNameIsIgor @
Указатели на функции же.
А, точно. Часто встречается в сяшечных проектах. И часто как эмуляция тех самых ООП-интерфейсов :)

Автор: DarkEld3r 02.07.15, 13:19
Цитата applegame @
Функция принимающая любой тип, который неявно кастуется в строку:

Неявно - никак. Но к типу можно предъявить требование наличия реализации трейта ToString.

Цитата applegame @
Ну и вторая задача: без оберток создать массив в который можно было бы засунуть объект любого типа неявно кастующегося в строку.

Правильно я понимаю, что в этом "массиве" будут лежать типы разного размера? Тогда нет, нельзя, можно сложить только указатели на такие типы.

Цитата applegame @
В Rust нельзя 8-мибитное целое неявно скастовать к 16-битному?

Неявно - нет.

Цитата applegame @
Или неявно скастовать наследника к предку?

Наследование есть только для трейтов.

Цитата D_KEY @
Ну да, можно хранить в массиве.

У трейтов "нет размер", вернее он неизвестен. Как их хранить в массиве? Можно хранить ссылки/указатели.

Цитата D_KEY @
Ну встроенные(несужающие) касты есть какие-то, да. Но свои сделать нельзя, если я правильно понимаю.

Аналог reinterpret_cast тоже есть. Естественно, он объявлен как unsafe.

А что значит "нельзя сделать свои касты"? Кто помешает, как и в С++ сделать функцию, например, my_super_cast? Можно и макрос.

Добавлено
Цитата D_KEY @
Встроенные примитивные типы вроде приводятся(без потери точности).

Нет, только через "as" (ключевое слово):
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    let e: i8 = 10;
    let ee: i32 = e; // error: mismatched types: expected `i32`, found `i8`
    let eee = e as i32; // OK.


Добавлено
Цитата applegame @
Нечто подобное сделали, хоть и через жопу - BCCL

В курсе, но я лучше подожду, пусть и С++17, а то и дальше, чем таким пользоваться. :)

Цитата applegame @
А трейты-потомки к трейтам-предкам тоже явно кастовать надо?

Нет.

Цитата applegame @
ИМХО, классы типов в Хаскеле скорее похожи как раз на концепты.

Почему?

Автор: MyNameIsIgor 02.07.15, 13:27
Цитата DarkEld3r @
В курсе, но я лучше подожду, пусть и С++17, а то и дальше, чем таким пользоваться.

А мне вот нравится как asio ругается в случае чего. Правда, там по-своему сделано, не BCCL.

Автор: applegame 02.07.15, 13:28
Цитата DarkEld3r @
У трейтов "нет размер", вернее он неизвестен. Как их хранить в массиве? Можно хранить ссылки/указатели.
Пусть будут указатели, не суть.
Давайте попробуем выявить преимущества растовских трейтов перед D-шными интерфейсами/С++-ными абстрактными классами. Если таковые преимущества есть.

Задача:
Есть некий trait/интерфейс ToString описывающий функцию to_string без аргументов и возвращающую строку.
Есть два произвольных типа Foo и Bar, нужно описать трейт ToString для этих типов. Создать эти трейты, и засунуть их в массив:
Вариант на D. На C++ все аналогично:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    import std.stdio;
    import std.string;
     
    // это растовский trait
    interface ToString {
        string toString();
    }
     
    // сторонний тип Foo
    struct Foo {
        int data;
    }
     
    // сторонний тип Bar
    struct Bar {
        long data;
    }
     
    // реализация traita ToString для стороннего типа Foo
    auto makeToString(Foo foo) {
        class Impl : ToString {
            Foo payload;
            override string toString() { return format("Foo(%s)", payload.data); }
            // конструктор
            this(Foo foo) { payload = foo; }
        }
        return new Impl(foo);
    }
     
    // реализация traita ToString для стороннего типа Bar
    auto makeToString(Bar bar) {
        class Impl : ToString {
            Bar payload;
            override string toString() { return format("Bar(%s)", payload.data); }
            // конструктор
            this(Bar bar) { payload = bar; }
        }
        return new Impl(bar);
    }
     
    void main() {
        ToString[] arr;
        auto bar = Bar(10);
        auto foo = Foo(20);
        arr ~= makeToString(bar);
        arr ~= makeToString(foo);
        foreach(o; arr) writeln(o.toString());
    }

Автор: DarkEld3r 02.07.15, 13:36
Цитата Qraizer @
Потому что без них, как и интерфейсов, вполне себе живётся на аналогах. Интерфейс – абстрактный класс, имеющий только чистые методы и не имеющий полей, который наследуется всегда виртуально. Трейты вообще не нуждаются в отдельном описании, любой шаблон предъявляет требования к свойствам своих аргументов посредством выполняемых над ними операций.

Стоп, не надо всё сразу. :)

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

D-шные трейты вполне себе есть как отдельная сущность. И выглядят вполне удобно. А ведь они, по сути, как раз те же самые концепты, которые планируют появиться в С++. То есть с ними лучше, а значит "в отдельном описании" всё-таки нуждаются.

Про концепты и "отлично показали" я говорил именно в контексте языка D. Который, в рамках "мейнстрима" не так уж далеко ушёл от раста и остаётся таким же "экспериментом".

Добавлено
Цитата MyNameIsIgor @
Конечно неправильно. Потому что интерфейсы в C++ - это концепты. Что непонятного? :-?

Как будет угодно, такой спор мне не интересен. Я и дальше продолжу называть абстрактные классы интерфейсами и пока что меня отлично понимали. :)

Добавлено
Цитата applegame @
В C++ мутабельности/иммутабельности как таковой нет, но есть константность

В курсе, но я думаю, что мы друг друга поняли.

Цитата applegame @
И можно создать шаблонную функцию принимающую как константный, так и мутабельный тип.

А можно и не шаблонную. :)
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void f(const int&);

Пожалуйста, передавать можно и константный и мутабельный тип.

А вот когда нам надо принимать разные типы и разные действия выполнять, то уже начинаются приседания. И нужно оно, чаще всего, тупо для std::forward. У раста семантика перемещения работает немного иначе, да и перегрузки функций нет. Ну то есть, если есть конкретная задача - можем обсудить и посмотрим получится ли реализовать, а не просто "а вот можно в расте вот этакий шаблон изобратьзить?".

Добавлено
Цитата MyNameIsIgor @
А мне вот нравится как asio ругается в случае чего. Правда, там по-своему сделано, не BCCL.

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

Автор: applegame 02.07.15, 13:50
Цитата Qraizer @
Не понял. Задача написать метафункцию isConvertible<> на Плюсах? Я это делал, задолго до знакомства с бустом. И у Александреску есть.
Нет. Задачу я описал чуть выше и решил с помощью создания дополнительных классов-оберток.
Цитата DarkEld3r @
вот когда нам надо принимать разные типы и разные действия выполнять, то уже начинаются приседания.
Об этом я и говорю. Допустим у нас есть шаблонная функция принимающая ссылку и заполняющая ее объектом. Ссылка должна быть мутабельной. Вот немного притянутый за уши пример использования isMutable.

Автор: MyNameIsIgor 02.07.15, 13:53
Цитата DarkEld3r @
Как будет угодно, такой спор мне не интересен

Сказал тот, кто сам предложил поспорить :facepalm: Ну, хорошо, не интересен так не интересен.
Цитата DarkEld3r @
Я и дальше продолжу называть абстрактные классы интерфейсами и пока что меня отлично понимали

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

Созрел новый вопрос. Есть ли в Rust перегрузка обобщённых функций по трейтам?

Автор: DarkEld3r 02.07.15, 14:03
Цитата applegame @
Пусть будут указатели, не суть.

Тогда никаких проблем:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    struct MyToStringVec {
        data: Vec<Box<ToString>>
    }
     
    struct Test;
     
    fn main() {
        let mut a = MyToStringVec { data: Vec::new() };
        
        a.data.push(Box::new(10));
        a.data.push(Box::new("test"));
        a.data.push(Box::new(Test)); // Error - Test не реализует ToString трейт
    }


Цитата applegame @
Давайте попробуем выявить преимущества растовских трейтов перед D-шными интерфейсами/С++-ными абстрактными классами. Если таковые преимущества есть.

Можно добавлять существующим типам реализацию нужныx трейтов. Наверное, всё, но опять же - трейты нужны, чуть ли не в первую очередь, для дженериков.

Цитата applegame @
Есть некий trait/интерфейс ToString описывающий функцию to_string без аргументов и возвращающую строку.
Есть два произвольных типа Foo и Bar, нужно описать трейт ToString для этих типов. Создать эти трейты, и засунуть их в массив:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    struct Foo {
        data: i32,
    }
     
    struct Bar {
        data: i64,
    }
     
    impl ToString for Foo {
        fn to_string(&self) -> String {
            format!("foo({})", self.data)
        }
    }
     
    impl ToString for Bar {
        fn to_string(&self) -> String {
            format!("bar({})", self.data)
        }
    }
     
    fn main() {
        let mut arr = Vec::<Box<ToString>>::new();
        arr.push(Box::new(Foo { data: 10 }));
        arr.push(Box::new(Bar { data: 20 }));
        
        for val in arr {
            println!("{}", val.to_string());
        }
    }

Пожалуйста. Только я не пойму, что это должно доказать?

Добавлено
Цитата applegame @
Об этом я и говорю. Допустим у нас есть шаблонная функция принимающая ссылку и заполняющая ее объектом. Ссылка должна быть мутабельной. Вот немного притянутый за уши пример использования isMutable.

Тогда почему функция не может быть такой?
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template <typename T>
    void f(T&) {}


Цитата MyNameIsIgor @
Сказал тот, кто сам предложил поспорить :facepalm: Ну, хорошо, не интересен так не интересен.

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

Цитата MyNameIsIgor @
Жаль, что вы общаетесь лишь с теми, чем тесный мирок ограничен симулоподными взглядами.

Ну спасибо, а то не знаю, что я бы без этой жалости делал. Действительно жаль, что я не начинаю рассказывать всё про все известные мне языки по любому поводу.

Цитата MyNameIsIgor @
Созрел новый вопрос. Есть ли в Rust перегрузка обобщённых функций по трейтам?

В смысле?
У разных трейтов могут быть одноимённые функции (ну а иначе было бы очень странно). Но перегрузки функций, в целом, нет. Так что и иметь две одноимённые функции, которые отличаются только типом аргумента-трейта нельзя.

Ну и специализации, в данный момент, тоже нет. Можно кидаться помидорами. :crazy: Впрочем, обсуждается и есть предложения. Очень надеюсь, что будет, иначе и впрямь убого получается.

Автор: applegame 02.07.15, 14:19
Цитата DarkEld3r @
Пожалуйста. Только я не пойму, что это должно доказать?
Это доказало, что никакой особой разницы между растовскими traitами и ООП-интерфейсами нет. Холивор не имеет смысла.
Цитата DarkEld3r @
В смысле?
Наверное имеется в виду что-то типа:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void foo(T)(T data) if(isSerializable!T) {...}
    void foo(T)(T data) if(isStringConvertible!T) {...}

Автор: DarkEld3r 02.07.15, 14:49
Цитата applegame @
Это доказало, что никакой особой разницы между растовскими traitами и ООП-интерфейсами нет. Холивор не имеет смысла.

Хм... если мы именно про "ООП интерфейсы", то их снаружи обычно расширять нельзя. В смысле, добавлять добавлять существующему типу наследование от интерфейса.
Но я повторюсь про то, что трейты - они (ещё и) для дженериков.

Ну и примеры всё-таки не эквивалентные. В D порождается новый тип, который наследуется от интерфейса и содержит данные. В расте - нет.

Цитата applegame @
Наверное имеется в виду что-то типа:

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

Автор: applegame 02.07.15, 14:57
Цитата DarkEld3r @
Хм... если мы именно про "ООП интерфейсы", то их снаружи обычно расширять нельзя. В смысле, добавлять добавлять существующему типу наследование от интерфейса.
Ну я же добавил. Де-факто тоже самое, просто на слегка более низком уровне.
Цитата DarkEld3r @
В D порождается новый тип, который наследуется от интерфейса и содержит данные. В расте - нет.
Почему это нет? Твои реализации трейта ToString для Foo и Bar - это фактически порождение новых типов "унаследованных" от абстрактного трейта ToString и содержащих данные.

Автор: DarkEld3r 02.07.15, 15:17
Цитата applegame @
Твои реализации трейта ToString для Foo и Bar - это фактически порождение новых типов "унаследованных" от абстрактного трейта ToString и содержащих данные.

Ну покажи где тут создаётся тип.

Цитата applegame @
Де-факто тоже самое, просто на слегка более низком уровне.

Особенно удобно будет, если понадобится несколько интерфейсов так "добавить". Я думаю ты видишь разницу между D-шными трейтами и тем, что в С++ костылять можно. А ведь формально тоже "можно сделать, просто на более низком уровне".

Вообще я не хочу сказать, что в расте прямо уж шедеврально и сильно оригинально сделали, но с учётом всех языковых возможностей получилось вполне неплохо. И подозреваю, что отталкивались они как раз от дженериков. И если захотели именно "более строгие шаблоны", то трейты тут вполне разумное решение.
Повторюсь - насколько оно удобно на практике будет не уверен, ничего большого на расте не писал.

Автор: MyNameIsIgor 02.07.15, 15:27
А можно в разных частях программы на Rust иметь разные реализации одного трейта для одного типа?

Автор: DarkEld3r 02.07.15, 15:35
Цитата MyNameIsIgor @
А можно в разных частях программы на Rust иметь разные реализации одного трейта для для одного типа?

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

Ну и есть ещё одно ограничение: чтобы реализовывать трейт для типа, то или трейт или тип должны быть определены в данном "crate" (либа или исполняемый файл). Аргументируется это тем, что иначе мог бы возникнуть хаос из-за того, что "все" определяют одинаковые трейты и реализовывают их для всех подряд типов.
Конечно, можно обойти обернув нужный тип и реализовав трейт для обёртки. Мне это не нравится, предпочёл бы более продвинутые механизмы импорта трейтов, хотя текущий вариант, разумеется, проще.

Автор: MyNameIsIgor 02.07.15, 15:37
Цитата DarkEld3r @
Ну покажи где тут создаётся тип.

:crazy: Чтобы вы потом не спрашивали, как можно читать, чтобы доказывать, я сразу поинтересуюсь: а у ваших знакомых что подразумевается под "создаётся тип"?

Автор: DarkEld3r 02.07.15, 15:44
Цитата MyNameIsIgor @
что подразумевается под "создаётся тип"?

Моя мысль правда непонятна? В расте при добавлении трейта типу размер типа не меняется. То есть в том фрагменте кода sizeof для a будет одинаковым независимо от количества реализуемых трейтов. Тип тоже, по прежнему, будет Foo.

В D функция makeToString возвращает новый "безымянный" тип и размер его уже не будет равным Foo/Bar. Ведь так?

Автор: Qraizer 02.07.15, 15:48
Цитата MyNameIsIgor @
А, понял откуда вы определения берёте. За сим закончим.
И тебе не болеть. Желаю найти авторитетное определение термина "функция".
Цитата DarkEld3r @
На мой взгляд, в С++ интерфейсы вполне есть, в том смысле, что их можно изображать абстрактными классами. Да, можно сказать, что у "у тру интерфейсов" есть больше ограничений и т.д.
Я бы перевернул. Абстрактный класс обладает большими возможностями по сравнению с интерфейсами. Но интерфейсы можно получить из абстрактных классов, не используя "лишние" возможности. Собственно поэтому интерфейсов как отдельной сущности в Плюсах и нет, просто за не надобностью.

Добавлено
Цитата DarkEld3r @
И если захотели именно "более строгие шаблоны", то трейты тут вполне разумное решение.
Вот мне тоже так показалось. Собственно концепты в Плюсах для этого же задумывались, вот только авторы идеи подошли к их архитектуре с позиции интерфейсов, чем и обломались. Новые концепты, вроде бы, выглядят куда более похожими на трейты.

Автор: MyNameIsIgor 02.07.15, 15:53
Цитата DarkEld3r @
Моя мысль правда непонятна?

Мысль то целиком понятна. Слова - нет. Хотя возможно, что я лишь думаю, что мысль понятна. Итак, что такое вы имеете в виду под "создание типа"?
Цитата DarkEld3r @
В расте при добавлении трейта типу размер типа не меняется. То есть в том фрагменте кода sizeof для a будет одинаковым независимо от количества реализуемых трейтов. Тип тоже, по прежнему, будет Foo.

А размер i32 тоже не изменится?
Цитата DarkEld3r @
В D функция makeToString возвращает новый "безымянный" тип и размер его уже не будет равным Foo/Bar. Ведь так?

Так. И что?

Добавлено
Цитата Qraizer @
Желаю найти авторитетное определение термина "функция".

Вы это говорите с таким смешным видом, как будто бы сами его хоть раз видели.

Автор: DarkEld3r 02.07.15, 16:07
Кстати, ещё забавный момент - в расте "пустые" типы занимают ровно 0 байт. Массивы пустых типов - тоже. То есть "костыль" они подложили именно в итерацию по массиву, а не в сами типы.

Цитата MyNameIsIgor @
Итак, что такое вы имеете в виду под "создание типа"?

Я надеюсь у вопроса практический смысл имеется? Но ок, я не поленюсь ответить. То, что typeof(Foo) не равен typeof(makeToString(Foo)).

Цитата MyNameIsIgor @
А размер i32 тоже не изменится?

Да, не изменится. Пруф.

Цитата MyNameIsIgor @
Так. И что?

"И всё". В расте не так, следовательно происходят не эквивалентные вещи.

Автор: MyNameIsIgor 02.07.15, 16:13
Цитата DarkEld3r @
Я надеюсь у вопроса практически смысл имеется? Но ок, я не поленюсь ответить. То, что typeof(Foo) не равен typeof(makeToString(Foo)).

Прикольно! Можно, например, написать функцию, и поскольку typeof(Foo) всё ещё будет не равен typeof(makeToString(Foo)), то делаем вывод: написание функции создаёт новый тип.
Цитата DarkEld3r @
Да, не изменится. Пруф.

Феноменально! А i64?

Автор: DarkEld3r 02.07.15, 16:16
Цитата MyNameIsIgor @
Феноменально! А i64?

Более чем уверен, что если бы размер изменился, то ты с такой же радостью и апломбом продолжал свои рассуждения. Пожалуй, лучше буду игнорировать.

Автор: MyNameIsIgor 02.07.15, 16:17
Цитата DarkEld3r @
если бы размер изменился

:facepalm:

Автор: D_KEY 02.07.15, 16:32
Цитата DarkEld3r @
Цитата D_KEY @
Ну да, можно хранить в массиве.

У трейтов "нет размер", вернее он неизвестен. Как их хранить в массиве? Можно хранить ссылки/указатели.

Я это и имел в виду.


Цитата
Цитата D_KEY @
Ну встроенные(несужающие) касты есть какие-то, да. Но свои сделать нельзя, если я правильно понимаю.

...
Цитата D_KEY @
Встроенные примитивные типы вроде приводятся(без потери точности).

Нет, только через "as" (ключевое слово):
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    let e: i8 = 10;
    let ee: i32 = e; // error: mismatched types: expected `i32`, found `i8`
    let eee = e as i32; // OK.



Ок, спасибо.

Цитата
А что значит "нельзя сделать свои касты"? Кто помешает, как и в С++ сделать функцию, например, my_super_cast? Можно и макрос.

Ничего не мешает, только это не каст, а функция :)

Автор: applegame 02.07.15, 17:19
Цитата DarkEld3r @
Ну покажи где тут создаётся тип.
Да вот тут:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    impl ToString for Foo {
        fn to_string(&self) -> String {
            format!("foo({})", self.data)
        }
    }
Скрытый тип. То что его нигде не видно, не значит, что его нет. А вот тут происходит его неявный каст к ToString:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    foo(&a)

Цитата DarkEld3r @
В расте при добавлении трейта типу размер типа не меняется. То есть в том фрагменте кода sizeof для a будет одинаковым независимо от количества реализуемых трейтов. Тип тоже, по прежнему, будет Foo.
Невероятно! Представь себе при добавлении новых интерфейсов в D размер типа тоже не меняется! И sizeof будет одинаковым независимо от количества реализуемых интерфейсов! И тип по прежнему будет Foo! Только сразу не бросайся опровергать, читай далее.
Цитата MyNameIsIgor @
В D функция makeToString возвращает новый "безымянный" тип и размер его уже не будет равным Foo/Bar. Ведь так?
Она возвращает ссылку на класс, которая всегда имеет один и тот же размер - размер указателя. Но какая разница? Твоя имплементация ToString для Foo это тоже фактически новый "безымянный" тип. Кстати я могу подправить makeToString и она больше не будет возвращать новых типов. Она будет возвращать интерфейс ToString. Что скажешь в этом случае?
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    ToString makeToString(Foo foo) {
        class Impl : ToString {
            Foo payload;
            override string to_string() { return format("foo(%s)", payload.data); }
            // конструктор
            this(Foo foo) { payload = foo; }
        }
        return new Impl(foo);
    }

Твой растовский foo(&a) будет эквивалентен D-шному foo(makeToString(a)). Rust неявно кастит Foo к реализации трейта ToString для Foo, и так же неявно кастит эту реализацию к абстрактному трейту ToString. В D приходится делать это явно. Забавно да? Оказывается местами наоброт Rust делает неявные касты, там где в других языках приходится делать явно. :)

Зацени :)
import std.stdio;
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    import std.string;
     
    interface ToString {
        string to_string();
    }
     
    struct Foo {
        int data;
    }
     
    auto makeToString(Foo foo) {
        class Impl : ToString {
            Foo payload;
            override string to_string() { return format("foo(%s)", payload.data); }
            // конструктор
            this(Foo foo) { payload = foo; }
        }
        return new Impl(foo);
    }
     
    void foo(ToString a) {
        writeln(a.to_string());
    }
     
    void main() {
        Foo a = {data: 10};
        foo(makeToString(a));
        
        writeln(Foo.sizeof);
        writeln(a.sizeof); // OMG оно равно предыдущему выражению!!! Как такое могло случиться???
    }

Может теперь ты сможешь оценить шутку MyNameIsIgor про размер i32 :D

Автор: DarkEld3r 02.07.15, 18:21
Цитата D_KEY @
Ничего не мешает, только это не каст, а функция :)

Ок, но тогда разве можно написать "свои касты" для С++/D?

Цитата applegame @
Она возвращает ссылку на класс, которая всегда имеет один и тот же размер - размер указателя. Но какая разница?

Вот, кстати, да - для примитивных типов и структур ещё и семантика нарушится ведь. В смысле, они станут классами.

Ну и в D у тебя уже вот тут будет новый тип:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    auto a = makeToString(Foo(30));

В расте не так, там тип за указателем не прячется:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    let a: Foo = Foo { data: 10 };

Тут а вполне себе стековая переменная. И если в структуру дополнительные поля добавить, то размер вырастет. В D просто для классов иначе размер получать надо, только и всего.

Цитата applegame @
Что скажешь в этом случае?

Скажу, что по указателю будет лежать всё тот же новый тип.

Цитата applegame @
Rust неявно кастит Foo к реализации трейта ToString для Foo

Не совсем так, все трейт-обьекты представляются одинаково, но да, сути это не меняет.

Цитата applegame @
Может теперь ты сможешь оценить шутку MyNameIsIgor про размер i32 :D

Не, не смогу. Происходят всё равно разные вещи.

Соглашусь, что я был не совсем корректен и при передаче по ссылке действительно будет "создаваться объект", но трейты можно с дженериками использовать и в этом случае никакие промежуточные обёртки не создаются.

Автор: D_KEY 02.07.15, 18:27
Цитата applegame @
Твой растовский foo(&a) будет эквивалентен D-шному foo(makeToString(a)).

Ну вообще у rust тут больший простор для реализации и оптимизации, соответственно. А так да.

Цитата
Rust неявно кастит Foo к реализации трейта ToString для Foo, и так же неявно кастит эту реализацию к абстрактному трейту ToString. В D приходится делать это явно. Забавно да?

Ну я бы не стал называть это именно кастом.

Автор: applegame 02.07.15, 19:32
Цитата DarkEld3r @
Вот, кстати, да - для примитивных типов и структур ещё и семантика нарушится ведь. В смысле, они станут классами.
Они не станут классами. Как были структурами и примитивными типами, так и останутся.
Цитата DarkEld3r @
Ну и в D у тебя уже вот тут будет новый тип:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    auto a = makeToString(Foo(30));

В расте не так, там тип за указателем не прячется:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    let a: Foo = Foo { data: 10 };

Тут а вполне себе стековая переменная. И если в структуру дополнительные поля добавить, то размер вырастет. В D просто для классов иначе размер получать надо, только и всего.
Это не эквивалентные куски кода.
Дешный вариант:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Foo a = {data: 10};

Вполне себе стековая переменная. И никаких дополнительных полей в структуру не добавляется.
Цитата DarkEld3r @
Скажу, что по указателю будет лежать всё тот же новый тип.
Дык в растовском варианте в функцию тоже передается отнюдь не сам объект Foo, а указатель типа ToString, и лежит там неведомый, скрытый от посторонних глаз объект. Те же яйца вид сбоку. Синтаксический цукер.

Добавлено
Цитата D_KEY @
Ну вообще у rust тут больший простор для реализации и оптимизации, соответственно.
Возможно, я не спорю, что в Rust все плохо. Я считаю данный холивар бессмысленным, так как в конечном счете сравниваем одно и тоже.

Автор: D_KEY 02.07.15, 19:52
Ну любой холивар бессмысленным можно назвать. Концепция trait'ов в сочетании с impl'ами и generic'ами, а так же, возможно, макросами, проста и представлена довольно ортогональными средствами. И если она при этом способна покрывать большую часть(все?) того, что решается с помощью классового ООП, перегрузки, шаблонной магии и пр. вещей, которые явно сложнее (и не эффективнее), то она, как минимум, заслуживает внимания.

Добавлено
Цитата applegame @
Дык в растовском варианте в функцию тоже передается отнюдь не сам объект Foo, а указатель типа ToString, и лежит там неведомый, скрытый от посторонних глаз объект. Те же яйца вид сбоку. Синтаксический цукер.

Тут, как мне кажется, ты излишне вдаешься в реализацию. Ты реализовал трейты с имплами через ОО-интерфейсы и врапперы. Но что ты этим показал?

Автор: applegame 02.07.15, 20:03
Цитата D_KEY @
И если она при этом способна покрывать большую часть(все?) того, что решается с помощью классового ООП, перегрузки, шаблонной магии и пр. вещей, которые явно сложнее (и не эффективнее), то она, как минимум, заслуживает внимания.
Сложнее и не эффективнее? Не согласен.
Цитата D_KEY @
Тут, как мне кажется, ты излишне вдаешься в реализацию. Ты реализовал трейты с имплами через ОО-интерфейсы и врапперы. Но что ты этим показал?
Полагаю они именно так и реализованы в Rust.

Автор: DarkEld3r 02.07.15, 20:04
Цитата applegame @
Они не станут классами. Как были структурами и примитивными типами, так и останутся.

Я всё-таки уточню - результат makeToString ведь будет классом?

Цитата applegame @
Я считаю данный холивар бессмысленным, так как в конечном счете сравниваем одно и тоже.

Ну почему же, я про D больше узнал, например. :)

Автор: applegame 02.07.15, 20:18
Цитата DarkEld3r @
Я всё-таки уточню - результат makeToString ведь будет классом?
Он будет интерфейсом или трейтом, как угодно. Аналог трейта ToString в Rust. Просто Rust неявно "кастует" Foo к ToString, а в D приходится явно "кастовать" при помощи makeToString.

Автор: DarkEld3r 02.07.15, 20:21
Цитата applegame @
Сложнее и не эффективнее? Не согласен.

Не готов оспаривать утверждением в целом, но местами всё-таки поудобнее. По крайней мере, если сравнивать с плюсовыми шаблонами, в D ведь ситуация получше. Не будет, как минимум, многокилометровых ошибок.
Да и макросы вполне мощные - регекспы, например, могут компилироваться вместе с программой, в С++ в шаблонах со строками работать удовольствия мало.

Добавлено
Цитата applegame @
Он будет интерфейсом или трейтом, как угодно.

Ну за указателем на интерфейс ведь будет класс? Ну и в расте можно переписать вот так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    fn foo2<T: ToString>(a: &T)

И результат будет совсем другой. В стандартной библиотеке трейты именно так и используются. Потому что эффективнее, в общем случае.

Автор: applegame 02.07.15, 20:24
Цитата DarkEld3r @
Да и макросы вполне мощные - регекспы, например, могут компилироваться вместе с программой
В D тоже могут. Любой код D может быть выполнен compile-time, если нет зависимости от неконстантных внешних данных.

Добавлено
Цитата DarkEld3r @
Ну за указателем на интерфейс ведь будет класс?
Будет. А за трейтом, что будет?

Автор: D_KEY 02.07.15, 20:39
Цитата applegame @
Сложнее и не эффективнее? Не согласен.

Т.е. ты не согласен, что классовое ООП, ОО-интерфейсы, шаблоны и концепты сложнее trait'ов с impl и дженериками? Хм. Мне кажется, ты лукавишь.

Добавлено
Цитата applegame @
Полагаю они именно так и реализованы в Rust.

Не знаю. Но не вижу никакого смысла выделять обертку в динамической памяти, да еще и при каждой передаче. Еще не вижу смысла заводить анонимный класс в make*. Так же можно поиграться с передачей аргументов и попробовать передавать трейты отдельно(примерно так делает haskell для тайпклассов). Но тут нужно подумать :) На asm-вывод посмотреть.

DarkEld3r, для rust нет чего-то вроде Compiler Explorer?

Автор: applegame 02.07.15, 21:07
Цитата D_KEY @
Т.е. ты не согласен, что классовое ООП, ОО-интерфейсы, шаблоны и концепты сложнее trait'ов с impl и дженериками? Хм. Мне кажется, ты лукавишь.
Нет не лукавлю. Впрочем в плюсах, наверняка сложнее. В D - сильно сомневаюсь. Приведи примеры, напишу тебе аналоги, сравним.

Автор: D_KEY 02.07.15, 21:18
Цитата applegame @
Цитата D_KEY @
Т.е. ты не согласен, что классовое ООП, ОО-интерфейсы, шаблоны и концепты сложнее trait'ов с impl и дженериками? Хм. Мне кажется, ты лукавишь.
Нет не лукавлю. Впрочем в плюсах, наверняка сложнее. В D - сильно сомневаюсь. Приведи примеры, напишу тебе аналоги, сравним.

Язык тут ни при чем. Речь о самих концепциях. trait задает набор методов, impl описывает их реализацию для типа. Тут нет никаких интерфейсов, абстрактных классов, классов. Тут нет иерархий наследования, виртуальных/невиртуальных методов, абстрактных методов, private/protected/public и пр. Очень много "лишней" информации уходит. Далее, шаблоны сложнее дженериков. Тут ты вряд ли будешь спорить? При этом Rust ушел от проблемы производительности дженериков в рантайме. Код генерируется. Но при этом нет сложных правил перегрузки, специализаций и т.п. Все разруливается через те же трейты, причем довольно прозрачно. И так же в статике. Концепты сложнее простого указания trait'а. impl'ы проще и декларативнее ручной реализации оберток.

Автор: amk 02.07.15, 21:23
Цитата DarkEld3r @
Моя мысль правда непонятна? В расте при добавлении трейта типу размер типа не меняется.

Цитата applegame @
Невероятно! Представь себе при добавлении новых интерфейсов в D размер типа тоже не меняется!

Точно так же и в С++. Размер изменится, только если добавить новые поля, подмешивание чистых абстрактных классов и замещение методов размер класса тоже не меняют

Автор: applegame 03.07.15, 06:44
Цитата D_KEY @
Речь о самих концепциях. trait задает набор методов, impl описывает их реализацию для типа. Тут нет никаких интерфейсов, абстрактных классов, классов. Тут нет иерархий наследования, виртуальных/невиртуальных методов, абстрактных методов, private/protected/public и пр.
наследование есть, интерфейсы (они же трейты) есть, функции описанные в треатах - виртуальные и абстрактные, отсутствие private/protected/public - недостаток, а не преимущество.
Цитата D_KEY @
Далее, шаблоны сложнее дженериков. Тут ты вряд ли будешь спорить?
Буду спорить с тем, что ты это представляешь как преимущество. Дженерики в расте - частный случай шаблонов. Написать шаблон аналогичный дженерику ничуть не сложнее.
Цитата D_KEY @
Но при этом нет сложных правил перегрузки, специализаций и т.п.
Сложность правил зависит от языка, а не от концепций.
Цитата D_KEY @
Концепты сложнее простого указания trait'а. impl'ы проще и декларативнее ручной реализации оберток.
Концепты != растовским трейтам. Я не буду использовать концепты для примитивных дженерикообразных шаблонов. Для этого есть более простые способы указания template constraints. И уж тем более я не буду городить оберток без крайней необходимости. Опять же для реализации аналогичного растовскому функционала не нужно никаких особых оберток. Тот пример, который я привел - имитация растовских трейтов. Такие декораторы не часто нужены в реальной жизни

А вообще, это все пустые разговоры, приведи пример, где на Rust все пишется проще, чем на D.

Добавлено
Цитата amk @
Точно так же и в С++. Размер изменится, только если добавить новые поля, подмешивание чистых абстрактных классов и замещение методов размер класса тоже не меняют
В целом, да. Но ЕМНИП это implementation defined. Стандарт C++ разве требует чтобы потомок имел тот же размер, что и предок, в случаях когда не добавляются дополнительные данные?

Автор: D_KEY 03.07.15, 07:10
Цитата applegame @
наследование есть

Нет. Есть возможность для trait'а указать, что его реализация типом требует так же реализации других трейтов.

Цитата
интерфейсы (они же трейты) есть

Допустим(а интерфейсы в D могут иметь реализацию методов по умолчанию?), но кроме них-то нет ничего. Никаких абстрактных классов, например.

Цитата
функции описанные в треатах - виртуальные и абстрактные

Виртуальные или абстрактные. Да. Но это не нужно указывать, об этом не нужно думать, как о каком-то отдельном механизме. Просто trait.

Цитата
отсутствие private/protected/public - недостаток, а не преимущество

При наличии модульной системы это довольно спорно.

Цитата
Буду спорить с тем, что ты это представляешь как преимущество.

Не теряй контекст, мы о простоте.

Цитата
Дженерики в расте - частный случай шаблонов.

Нет. Дженерики - это параметрический полиморфизм, который, в общем случае, шаблонами не покрывается.

Цитата
Написать шаблон аналогичный дженерику ничуть не сложнее.

Помнишь пример с контролем равенства размера двух списков/векторов во время компиляции?

Цитата
Цитата D_KEY @
Но при этом нет сложных правил перегрузки, специализаций и т.п.
Сложность правил зависит от языка, а не от концепций.


Если ты вводишь в язык перегрузку, полноценные шаблоны и специализации, то правила уже простыми не будут.

Цитата
Концепты != растовским трейтам.


Не равны. И? Вопрос в том, позволяет ли сочетание более простых и ортогональных средств в Rust решать те же задачи проектирования и управления сложностью, что мы решаем с помощью сложных и связных компонентов в более "классических" языках, без потери эффективности как в работе, так и в производительности систем?

Цитата
А вообще, это все пустые разговоры, приведи пример, где на Rust все пишется проще, чем на D.


Так в Rust вообще нет сложных концепций. Тут скорее ты должен привести пример, когда нам нужно все это многообразие средств и их мощность.

Автор: DarkEld3r 03.07.15, 08:23
Цитата applegame @
В D тоже могут. Любой код D может быть выполнен compile-time, если нет зависимости от неконстантных внешних данных.
Ну я специально про С++ уточнил - там ведь со строками в шаблонах нормально работать нельзя. А можно простой пример шаблона в D, который со строкой работает?

Цитата applegame @
Будет. А за трейтом, что будет?
Будет структура содержащая указатель на данные и таблицу виртуальных функций. Но я не придираюсь, если что, просто хотел уточнить правильно ли понимаю происходящее в D.

Кстати, поигрался немного с трейтами в D - в ideone если одна из требуемых функций имеет неподходящую сигнатуру, то компилятор не очень понятно ругается:
template prog.test(Range) if (isInputRange!(Range)) does not match any function template declaration
То есть не показывает, что именно не подходит. Это там компилятор такой или везде так?

В расте, конечно, механизм "немного" другой, но если трейт не реализован, то компилятор так и скажет (the trait `...` is not implemented for the type `...`). Если же попытаться неправильно реализовать трейт, то тоже будет явное указание на проблему. Или "not all trait items implemented, missing: `drop`" или указание на неправильную сигнатуру конкретного метода.

Кстати, ещё один вопрос - в С++ ведь нельзя шаблонным параметрам-функциям нормально ограничить сигнатуру. В смысле можем или так или так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template <typename F>
    void f(F f) {}
     
    void f(const std::function<int (int)>& f) {}
А в D можно для шаблонов явно сигнатуру требовать?

Цитата D_KEY @
Т.е. ты не согласен, что классовое ООП, ОО-интерфейсы, шаблоны и концепты сложнее trait'ов с impl и дженериками? Хм. Мне кажется, ты лукавишь.
На мой взгляд, в расте при написании дженериков сильнее с типами "бороться" надо. в С++, в этом плане, проще - если шаблон не будет никуда наружу торчать, то можно не париться с концептами/статик асертами и т.д. Сигнатуры, опять же, сильнее "замусориваются" разными требованиями. Для внешних функций оно, пожалуй, хорошо (хотя всё равно найдутся те, кто будут ныть про нечитаемость/сложность), но побыстрому набросать шаблон уже не выйдет.

Цитата D_KEY @
DarkEld3r, для rust нет чего-то вроде Compiler Explorer?
Дык - play.rust-lang.org. Там и асмовый и ллвмный выхлоп посмотреть можно. Явно флаги компиляции задавать нельзя, но дебаг/релиз переключать можно.

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

Добавлено
Цитата D_KEY @
Речь о самих концепциях.
Ну... я хоть и защищаю тут раст, но не торопился бы с выводами. :)

Одну сложность убрали, другую добавили. Скажем, в С++ шаблону класса дополнительный параметр можно "просто так" довесить и не париться, а раст требует, чтобы все параметры были задействованы. Конечно, необходимое сделать можно, но более явно - через PhantomData, но это ведь тоже, своего рода, "лишние приседания. Система типов, опять же, выглядит более сложной.

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

Добавлено
Цитата amk @
Точно так же и в С++.
Да, мы уже разобрались. Просто говорили немного о разном. :)

Автор: D_KEY 03.07.15, 08:32
DarkEld3r, я выводов о том, как это будет на практике, не делал. понятно же, что за все нужно чем-то платить. И вообще, см. подпись :)

Автор: DarkEld3r 03.07.15, 08:43
Цитата applegame @
функции описанные в треатах - виртуальные и абстрактные
Немного не так. Функции будут виртуальными или нет в зависимости от использования. Если через "трейт-обьект", то да, виртуальные. Если через дженерик - нет. В С++/D мы должны заранее выбирать "каким видом полиморфизма воспользоваться".

Ну и функции в трейтах вполне могут иметь реализацию, просто она не может зависить от данных.

Цитата applegame @
отсутствие private/protected/public
Приват/паблик вполне есть, хотя и немного другие. Не уверен, что отсутствие protected - недостаток, особенно при отсутствии наследования (данных).

Цитата applegame @
Написать шаблон аналогичный дженерику ничуть не сложнее.
Я бы сказал, что всё-таки сложнее. Опять же, главная "проблема", в том, что в С++ нет для этого "инфраструктуры", а в расте - есть.

Цитата applegame @
И уж тем более я не буду городить оберток без крайней необходимости.
Обёртки, если мы именно про отдельные рантаймовые сущности и в расте "без нужны" не городятся.

Обычный способ работы с трейтами - это как раз через дженерики. Просто так передавать указатели/ссылки на трейт-объекты особого смысла нет, нужно оно если мы хотим вещей типа "сохранить в массиве разные объекты с общим интерфейсом" - тут и в С++ будут тоже указатели. Или если нам надо что-то типа std::function.

Цитата applegame @
Такие декораторы не часто нужены в реальной жизни
Задача ведь не "сделать декоратор", а реализовать интерфейс для существующего типа. В С++/Д, если мы не можем использовать шаблоны, то декораторы всё-таки придётся городить.

Ну а вообще это просто разные подходы. В плюсовом/дшном ООП ты сделаешь интерфейс и предложишь клиентам твоей либы от него наследоваться и передавать тебе указатель. В расте ты обьявишь трейт и клиенты точно так же реализуют его для своих типов.

Добавлено
Цитата D_KEY @
И вообще, см. подпись :)
:)

Цитата applegame @
Но ЕМНИП это implementation defined. Стандарт C++ разве требует чтобы потомок имел тот же размер, что и предок, в случаях когда не добавляются дополнительные данные?
Кстати, да. "Empty base optimization" - это только возможная, но не обязательная оптимизация.

И всё-таки я вернусь к тому, что в расте пустые типы занимают 0. Вроде, даже в относительно новых языках, размер будет 1. Интересно, тут есть какие-то преимущества/недостатки? Или разница настолько несущественна, что всем пофиг?

Автор: applegame 03.07.15, 09:15
Цитата D_KEY @
Нет. Есть возможность для trait'а указать, что его реализация типом требует так же реализации других трейтов.
Не понял. trait'ы вроде наследуются, нет?
Цитата D_KEY @
Допустим(а интерфейсы в D могут иметь реализацию методов по умолчанию?), но кроме них-то нет ничего. Никаких абстрактных классов, например.
В D интерфейсы не могут иметь реализацию методов по умолчанию. Абстрактные классы есть, но ты ими можешь не пользоваться и даже не знать об их существовании.
Цитата D_KEY @
Виртуальные или абстрактные. Да. Но это не нужно указывать, об этом не нужно думать, как о каком-то отдельном механизме. Просто trait.
В D тоже ничего не нужно указывать, по умолчанию все виртуальное, а в интерфейсах еще и абстрактное. Можно не думать и даже не знать, что бывают еще и невиртуальные и неабстрактные функции.
Цитата D_KEY @
Не теряй контекст, мы о простоте.
D позволяет простое писать просто, а сложное сложно.
Цитата D_KEY @
Нет. Дженерики - это параметрический полиморфизм, который, в общем случае, шаблонами не покрывается.
И в случае Rust?
Цитата D_KEY @
Помнишь пример с контролем равенства размера двух списков/векторов во время компиляции?
Нет, не помню. напомни пожалуйста или дай ссылку.
Цитата D_KEY @
Если ты вводишь в язык перегрузку, полноценные шаблоны и специализации, то правила уже простыми не будут.
Все эти правила необязательны к использованию. Если они тебе кажутся сложными или избыточными - просто не используй их.
Цитата D_KEY @
Так в Rust вообще нет сложных концепций. Тут скорее ты должен привести пример, когда нам нужно все это многообразие средств и их мощность.
Я плохо знаю rust, поэтому мне трудно приводить примеры. Что-нибудь не сильно навороченное, может сериализация?
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    // если компилируется код data.serialize(serializer);
    void serialize(S,T)(ref S serializer, T data) if(isCustomSerializable!T) {..}
    // структуры и классы
    void serialize(S,T)(ref S serializer, T data) if(isAggregate!T) {..}
    // Скаляры
    void serialize(S,T)(ref S serializer, T data) if(isScalar!T) {..}
    // Массивы
    void serialize(S,T)(ref S serializer, T data) if(isArray!T) {..}

Автор: applegame 03.07.15, 09:39
Цитата DarkEld3r @
Ну я специально про С++ уточнил - там ведь со строками в шаблонах нормально работать нельзя. А можно простой пример шаблона в D, который со строкой работает?

Для того чтобы что-то собралось в compile-time не обязательно, чтобы это был шаблон. Любую функцию можно выполнить, если она не завязана на внешние данные. В C++ уже тоже есть constexpr, но пока сильно урезанный по сравнению с D. Шаблоны с строковыми параметрами активно применяются в перезагрузке операторов. Например opDispatch для перезагрузки обращения к несуществующему полю, в качестве параметра использует название метода:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    import std.stdio;
     
    struct Foo {
        void opDispatch(string method, ARGS...)(ARGS args) {
            writefln(`method "%s" with %s parameters`, method, ARGS.length);    
        }
    }
     
    void main() {
        Foo foo;
        foo.bar("test");
        foo.baz(1, "test");
    }

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    method "bar" with 1 parameters
    method "baz" with 2 parameters

Автор: D_KEY 03.07.15, 09:47
Цитата applegame @
Не понял. trait'ы вроде наследуются, нет?

Ну там такое "наследование", которое просто требует от типа, который реализует трейт, реализовать еще и всех trait'ов-"предков".

Цитата
В D интерфейсы не могут иметь реализацию методов по умолчанию.

trait в Rust могут.

Цитата
Абстрактные классы есть, но ты ими можешь не пользоваться и даже не знать об их существовании.

Цитата
В D тоже ничего не нужно указывать, по умолчанию все виртуальное, а в интерфейсах еще и абстрактное. Можно не думать и даже не знать, что бывают еще и невиртуальные и неабстрактные функции.

Ну не верю :) Наверняка в коде это все часто и активно используется. А значит, программист должен это знать.

Цитата
Цитата D_KEY @
Нет. Дженерики - это параметрический полиморфизм, который, в общем случае, шаблонами не покрывается.
И в случае Rust?

Думаю, что да. Но мне не хватает экспериментов с ним, а я пока не хочу тратить на это время.

Цитата
Нет, не помню. напомни пожалуйста или дай ссылку.

у нас в холиварах
ЛОР
ЖЖ

Цитата
Все эти правила необязательны к использованию. Если они тебе кажутся сложными или избыточными - просто не используй их.

Как мне разбираться в чужом коде?

Цитата
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    // если компилируется код data.serialize(serializer);
    void serialize(S,T)(ref S serializer, T data) if(isCustomSerializable!T) {..}
    // структуры и классы
    void serialize(S,T)(ref S serializer, T data) if(isAggregate!T) {..}
    // Скаляры
    void serialize(S,T)(ref S serializer, T data) if(isScalar!T) {..}
    // Массивы
    void serialize(S,T)(ref S serializer, T data) if(isArray!T) {..}

Как я понимаю, просто создается trait сериализуемых объектов(возможно с какой-то реализацией по умолчанию). Далее он специализируется для других трейтов, потом уже специализируется для каких-то конкретных типов, если нужно. Разве этого недостаточно? Хотя могу ошибаться, тут лучше пусть DarkEld3r расскажет.

Автор: applegame 03.07.15, 09:48
Цитата DarkEld3r @
Кстати, поигрался немного с трейтами в D - в ideone если одна из требуемых функций имеет неподходящую сигнатуру, то компилятор не очень понятно ругается:
template prog.test(Range) if (isInputRange!(Range)) does not match any function template declaration
То есть не показывает, что именно не подходит. Это там компилятор такой или везде так?
Это задуманная фича, перекочевала в D из C++. Называется SFINAE. То бишь если шаблон не подходит, или инстанцирцется с ошибкой, то компилятор просто его пропускает и идет дальше. Вдруг ниже по коду будет подходящая специализация. При желании можно самому выводить вменяемые ошибки, но это как-то не принято:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    import std.traits;
    import std.range;
        
    struct Test
    {
      bool empty() { return true; }
      int popFront() { return 10; }
      int front(int) { return 10; }
    }
        
    auto test(Range)(Range r) {
      static assert(isInputRange!Range, "type '" ~ Range.stringof ~ "' must be an InputRange");
    }
        
    void main() {
      Test t;
      test(t);
    }

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    prog.d(12): Error: static assert  "type 'Test' must be an InputRange"
    prog.d(17):        instantiated from here: test!(Test)

Автор: DarkEld3r 03.07.15, 09:53
Цитата applegame @
Не понял. trait'ы вроде наследуются, нет?
Они-то "наследуются", но для типа в итоге всё равно надо реализовывать два отдельных трейта. Просто если ты реализуешь только тот, что наследуется от базового, то компилятор на это укажет.

То есть не так как в "обычном ООП" - отнаследовался от пачки интерфейсов и потом реализовал их внутри одного калсса.

Цитата applegame @
Что-нибудь не сильно навороченное, может сериализация?
В расте будет соответствующий трейт, который надо будет реализовать для нужных типов. Вернее, он даже был в стандартной библиотеке, потом убрали и предлагают пользоваться сторонней библиотекой (их даже несколько есть). Хз почему, я особо не копал. Может решили, что универсальная реализация не всех устроит, а может стабилизировать не успели.

Цитата applegame @
Для того чтобы что-то собралось в compile-time не обязательно, чтобы это был шаблон.
Да, точно. Но на плюсовых constexpr нормально компиляцию регекспов точно не сделаешь. Ну а в D, действительно, проблем с этим быть не должно.

Кстати, как в D затребовать/гарантировать выполнения функции именно на этапе компиляции?

Добавлено
Цитата D_KEY @

И не дает переопределять дефолтные методы родительских трейтов.
Даёт. :)

Автор: D_KEY 03.07.15, 09:59
Цитата DarkEld3r @
Цитата D_KEY @

И не дает переопределять дефолтные методы родительских трейтов.
Даёт. :)

А можно пример, как это работает? И в каком месте мне для своего типа менять реализацию, при реализации trait'а родителя или при реализации trait'а потомка? Что будет использоваться, моя реализация trait'а родителя или дефолтная реализация trait'а потомка, если мы работаем с объектом через trait потомка?

Добавлено
Цитата D_KEY @
Цитата
Нет, не помню. напомни пожалуйста или дай ссылку.

у нас в холиварах
ЛОР
ЖЖ

Цитата D_KEY @
Собственно, С++ шаблоны напрямую не работают как раз потому, что они не являются (полиморфными) типами - они именно шаблоны, по которым генерируются конкретные не параметризованные типы.

Вот это, наверное, ключевое отличие для нашего холивара.

Автор: DarkEld3r 03.07.15, 10:01
Цитата applegame @
При желании можно самому выводить вменяемые ошибки, но это как-то не принято:
Ну так мы отказываемся от использования трейтов - не интересно.

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

Автор: DarkEld3r 03.07.15, 11:28
Цитата D_KEY @
А можно пример, как это работает?
Был не прав - не работает оно. Если попытаться переопределить в трейте "наследнике" базовый метод, то получится просто новый метод. И если реализовать для типа оба трейта, то при вызове этого метода будет ошибка компиляции - "multiple applicable items in scope". Можно неоднозначность руками разруливать.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    trait Base {
        fn foo(&self) { println!("Base::foo") }
    }
     
    trait Derived : Base {
        fn foo(&self) { println!("Derived::foo") }
        
        fn bar(&self) { println!("Derived::bar") }
    }
     
    struct Test;
     
    impl Base for Test {}
    impl Derived for Test {}
     
    fn main() {
        let a = Test;
        
        //a.foo(); // multiple applicable items in scope
        a.bar();
        
        Base::foo(&a);
        Derived::foo(&a);
    }

Автор: applegame 03.07.15, 12:11
Цитата DarkEld3r @
В расте будет соответствующий трейт, который надо будет реализовать для нужных типов.
И функция сериализации будет в качестве аргумента принимать этот трейт? Дело попахивает динамическим полиморфизмом. Не очень похоже на "проще и эффективнее". Скорее наоборот.
Цитата DarkEld3r @
Про SFINAE в курсе, но по идее, компилятор мог бы выдавать нормальную диагностику, если функция ровно одна. Но вообще это как раз пример "проблем" с шаблонами, которых в расте просто не может быть.
Звучит как "это как раз пример проблем со здоровьем, которых у трупов не может быть". Конечно же в расте не может быть проблем с шаблонами, потому что и там нет :). На самом деле это проблема компилятора, а не самого языка.

Автор: MyNameIsIgor 03.07.15, 12:20
Цитата DarkEld3r @
Кстати, ещё один вопрос - в С++ ведь нельзя шаблонным параметрам-функциям нормально ограничить сигнатуру.

Ага, тем, у которых всё никак не увеличивается размер, запретили SFINAE.

Автор: applegame 03.07.15, 12:43
Цитата DarkEld3r @
Кстати, как в D затребовать/гарантировать выполнения функции именно на этапе компиляции?

По всякому. Например так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    enum result = foo();

Автор: MyNameIsIgor 03.07.15, 12:44
Цитата D_KEY @
Помнишь пример с контролем равенства размера двух списков/векторов во время компиляции?

Там опять же всё решается затиранием типа для имитации generic.

Автор: DarkEld3r 03.07.15, 13:11
Цитата applegame @
И функция сериализации будет в качестве аргумента принимать этот трейт? Дело попахивает динамическим полиморфизмом.
Я уже не знаю в какой раз повторяю, что в расте, в большинстве случаев, с трейтами принято работать через дженерики. То есть будет как-то так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    fn foo<T: Serializable>(data: &T) { ... }
И никакого динамического полиморфизма.

Цитата applegame @
Звучит как "это как раз пример проблем со здоровьем, которых у трупов не может быть".
Ну зачем же передёргивать? Я нигде не утверждал, что дженерики и шаблоны одно и то же. Но такие решения, при создании языка, вполне могли приниматься исходя из желания сделать более точные сообщения об ошибках.

Цитата applegame @
На самом деле это проблема компилятора, а не самого языка.
Так-то да, но язык существует не в вакууме. ideone предлагает два варианта компилятора D, проверил на обоих. Потому и спросил - их ведь больше, да и версии там непонятно какие, может это давно решено.

Цитата applegame @
По всякому. Например так:
Ну про enum (очень поверхностно) в курсе. Значением ведь может быть любой тип?
Ну и раз, "по разному", то может ещё пару вариантов приведёшь? A то бегло не нагуглить не удалось.

Цитата MyNameIsIgor @
Ага, тем, у которых всё никак не увеличивается размер, запретили SFINAE.
Покажи кодом.
Ну и меня интересовало есть ли в D что-то на эту тему.

Автор: MyNameIsIgor 03.07.15, 13:47
Цитата DarkEld3r @
Покажи кодом.

Так ведь
Цитата DarkEld3r @
Про SFINAE в курсе

Автор: DarkEld3r 03.07.15, 14:43
Цитата MyNameIsIgor @
Так ведь
Какой-то детский сад. Я-то думал, что тебе есть, что сказать, мало ли, вдруг чего-то не знаю.

Иначе проще будет забить и позволить компилятору ругаться в месте применения переданной функции, чем городить огород имеющими средствами. В крайнем случае, воткнуть static_assert с использованием boost::function_traits (и да, я в курсе, что там тоже SFINAE внутри).

Ну и я уже нашёл, что в D оно таки есть из коробки - arity, ReturnType, ParameterTypeTuple и т.д.

Собственно, что хотел сказать - в С++ у нас или такие костыли (справедливости ради, они не особо часто и нужны) или std::function с лишним выделением памяти. В расте мало того, что оно делается легко, так что ещё и выглядит почти одинаково:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    fn test_static<T: Fn(i32) -> i32>(f: T) {
        println!("{}", f(10));
    }
     
    fn test_dynamic(f: &Fn(i32) -> i32) {
        println!("{}", f(20));
    }
     
    fn foo(a: i32) -> i32 {
        a + 10
    }
     
    fn main() {
        test_static(foo);
        test_dynamic(&foo);
    }

Автор: applegame 03.07.15, 15:01
Цитата DarkEld3r @
И никакого динамического полиморфизма.
Я правильно понимаю? Нам потребуется отдельный трейт для каждого сериализуемого типа? То есть в D я просто создал структуру и вызвал serialize. А в Rust для каждой сериализуемой структуры придется городить свой кастомный трейт?

Автор: MyNameIsIgor 03.07.15, 15:09
Цитата DarkEld3r @
Какой-то детский сад.

Т.е. вас только в детском саду просили отвечать за свои слова?
Цитата DarkEld3r @
Я-то думал, что тебе есть, что сказать

Возможно есть, возможно - нет. В любом случае я хочу, чтобы слушающий обладал достаточным интеллектом, чтобы понять сказанное.
Цитата DarkEld3r @
мало ли, вдруг чего-то не знаю

Не знаете. Начните заполнять прорехи в вашем образовании с понятия "интерфейс".
Цитата DarkEld3r @
воткнуть static_assert

Это правильное решение, если не требуется перегрузка по сигнатуре функции. Ах, да, в Rust же перегрузки нет :crazy:
Цитата DarkEld3r @
с использованием boost::function_traits

Не нужно, ибо не переварит перегрузку operator().
Цитата DarkEld3r @
и да, я в курсе, что там тоже SFINAE внутри

Кто подсказал?

Автор: applegame 03.07.15, 15:26
Цитата DarkEld3r @
Ну про enum (очень поверхностно) в курсе. Значением ведь может быть любой тип?
Почти. Классы и указатели на структуры точно не могут.
Цитата DarkEld3r @
Ну и раз, "по разному", то может ещё пару вариантов приведёшь? A то бегло не нагуглить не удалось.
Присвоить результат функции глобальной или статической const/immutable переменной, передать результат в качестве параметра шаблона, вызвать эту функцию внутри шаблона в процессе инстанцирования.

Добавлено
Цитата DarkEld3r @
Ну и я уже нашёл, что в D оно таки есть из коробки - arity, ReturnType, ParameterTypeTuple и т.д.

Собственно, что хотел сказать - в С++ у нас или такие костыли (справедливости ради, они не особо часто и нужны) или std::function с лишним выделением памяти. В расте мало того, что оно делается легко, так что ещё и выглядит почти одинаково:
В D для такого примитива не нужны ни arity ни ReturnType ни ParameterTypeTuple:
http://dpaste.dzfl.pl/6e83bb30275a
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    import std.stdio;
     
    void test_static(T: int function(int))(T foo) {
        foo(10).writeln;
    }
     
    void test_dynamic(int function(int) foo) {
        foo(20).writeln;
    }
     
    int foo(int a) {
        return a + 10;
    }
     
    void main() {
        test_static(&foo);
        test_dynamic(&foo);
    }

Автор: DarkEld3r 03.07.15, 15:47
Цитата applegame @
Я правильно понимаю? Нам потребуется отдельный трейт для каждого сериализуемого типа? То есть в D я просто создал структуру и вызвал serialize.
Да, придётся.

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

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

Цитата MyNameIsIgor @
Начните заполнять прорехи в вашем образовании с понятия "интерфейс".
Газификация луж продолжается.

Добавлено
Цитата applegame @
В D для такого примитива не нужны ни arity ни ReturnType ни ParameterTypeTuple:
Ок, спасибо. Второй вариант - это просто указатель на функцию или аналог плюсового std::function? Если первое, то немного не то.

А вот с шаблоном интересно, жаль в С++ так нельзя.

Автор: applegame 03.07.15, 15:57
А вот для ситуации, когда функция должна принять любой callable тип c заданными параметрами, а не только собственно функцию, эти трейты пригодятся:
http://dpaste.dzfl.pl/2d66a35eff90
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    import std.stdio;
    import std.traits;
    import std.typetuple;
     
    void test(T)(T foo) if(
        isCallable!T &&
        is(ReturnType!T == int) &&
        is(ParameterTypeTuple!T == TypeTuple!int)
    ) {
        foo(10).writeln;
    }
     
    int foo(int a) {
        return a + 10;
    }
     
    struct Bar {
        int opCall(int a) {
            return a + 20;
        }
    }
     
    int baz(short a) {
        return a + 30;
    }
     
     
    void main() {
        test(&foo);
        Bar bar;
        test(bar);
        // test(&baz); // fail
    }

Rust так умеет? В C++ кстати это тоже возможно, правда несколько сложнее.

Добавлено
Цитата DarkEld3r @
Ок, спасибо. Второй вариант - это просто указатель на функцию или аналог плюсового std::function? Если первое, то немного не то.
Это указатель, а в Rust это что? В D аналог std::function не нужен, его заменяют лямбды (а точнее делегаты), которые, в отличие от C++, имеют четко описанный тип зависящий только от параметров и возвращаемого значения.

Добавлено
Цитата DarkEld3r @
Правильно я понимаю, что в D для структур ты будешь сериализовать все члены? В общем случае, это ведь некорректно. С тем, что удобнее иметь такое поведение по умолчанию с возможностью запретить сериализацию отдельных полей спорить не буду.
Не обязательно все. В D можно еще указывать user defined attributes, которые также возможно читать compile-time. С помощью них можно задавать всякие флажки. Типа:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    struct Data {
        int field;
        @ignored {
             string ignoredString;
             int ignoredInt;
        }
        @name("zoo") short _un_readableNamed_;
    }

Автор: DarkEld3r 03.07.15, 21:55
Цитата applegame @
Rust так умеет?
"Теоретически". Выше в примерах кода (Fn(i32) -> i32), Fn - это как раз трейт. Их есть три вида: Fn, FnMut и FnOnce. Лямбды - это как раз сахар для этих трейтов. Для своего типа этот трейты, по идее, тоже можно реализовать, но сейчас это "нестабильная фича". Попробовал набросать пример, для этого надо "переключаться" на "ночной" раст - в стабильных реализах нестабильные вещи использовать запрещено. Получил "internal compiler error" и предложение отправить баг-репорт. :)

Цитата applegame @
Это указатель, а в Rust это что?
"Трейт-обьект", по сути - это как раз аналог std::function. Ну или той обёртки, что ты делал когда демонстрировал добавление реализации интерфейса.

Цитата applegame @
его заменяют лямбды (а точнее делегаты), которые, в отличие от C++, имеют четко описанный тип зависящий только от параметров и возвращаемого значения.
В расте у callable сущностей тоже один тип - Fn/FnMut/FnOnce (в зависимости от сигнатуры). Вне зависимости от того лямбда это, функция или тип с вручную реализованными нужными трейтами.

Что это будет в итоге - зависит от использования. Если дженерик, то заинлайнится. Если ссылка/указатель на трейт - то будет "обёртка".

Цитата applegame @
В D можно еще указывать user defined attributes
В расте, "пока что", нельзя (есть сколько-то готовых), но планы добавить свои есть.

Кстати, любопытно - как в D разруливаются конфликты имён в атрибутах?

Цитата applegame @
С помощью них можно задавать всякие флажки.
Но в чужой тип их ведь не впихнёшь?

Автор: applegame 04.07.15, 06:44
Цитата DarkEld3r @
Но в чужой тип их ведь не впихнёшь?
В смысле не впихнешь? В D UDA - это либо произвольный тип, либо значение.

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    struct MySuperAttribute {
        int a;
    }
     
    enum attr1 = MySuperAttribute(2);
    enum attr2 = MySuperAttribute(3);
    alias attr3 = MySuperAttribute;
     
    struct Bar {
    }
     
    struct Foo {
        @attr1 int f1;
        @attr2 int f2;
        @attr3 int f3;
        @attr3(2) int f4;
        @Bar int f5;
        @("string", 10, Bar) int f6; // несколько атрибутов
    }

Цитата DarkEld3r @
Кстати, любопытно - как в D разруливаются конфликты имён в атрибутах?

Решаются также, как конфликты имен типов.

Автор: Qraizer 05.07.15, 00:29
Цитата applegame @
Стандарт C++ разве требует чтобы потомок имел тот же размер, что и предок, в случаях когда не добавляются дополнительные данные?
В целом да. Явно этого не говорится, однако это следует из некоторых пунктов, описывающих лэйаут структур – конечно же она там C-совместимая – и определения наследования. Однако не всегда. Исключения позволяются при виртуальном наследовании и если потомок "вдруг" становится полиморфным. Также из описания объектной модели (не путать с описанием модели ООП!) следует ещё одна возможность исключения, когда базовые классы имеют "нулевой" размер.

Добавлено
Цитата MyNameIsIgor @
В любом случае я хочу, чтобы слушающий обладал достаточным интеллектом, чтобы понять сказанное.
Лично я в этой фразе
Цитата MyNameIsIgor @
Ага, тем, у которых всё никак не увеличивается размер, запретили SFINAE.
даже не во всех словах разобрался, так что смысл фразы расплылся где-то на километр. Куда уж мне с тобой спорить было бы.

Автор: DarkEld3r 05.07.15, 12:40
Цитата applegame @
В смысле не впихнешь?
Ну в примере кода ты объявляешь структуру Data с ignored внутри. Если дата уже где-то определена, то так не получится?

Цитата applegame @
Решаются также, как конфликты имен типов.
То есть UDA честно в неймспейсы попадают?

Вот, кстати, в расте тут с макросами "немного" коряво - на них неймспейсы не работают. Правда они импортируются явно, но переименовать, вроде, нельзя.

Автор: Qraizer 05.07.15, 22:46
Цитата DarkEld3r @
Кстати, ещё один вопрос - в С++ ведь нельзя шаблонным параметрам-функциям нормально ограничить сигнатуру.
Я тут кстати подумал, а кто не даёт-то? Выбирай костыль:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template <typename T> struct check;
    template <>           struct check<int(int)>{};
     
    template <typename T>
    struct impl : check<T>
    {
      static void doIt(T *t) { /* ... */ }
    };
     
    template <typename T>
    void f(T *t)
    {
      return impl<T>::doIt(t);
    }
Чуть проще.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template <typename T> struct check;
    template <>           struct check<int(int)>{};
     
    template <typename T>
    void f(T *t)
    {
      check<T>();
      /* ... */
    }
Ну или совсем элементарно.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template <int (*t)(int)>
    void f()
    {
      /* ... */
    }
Зачем ради сахара тащить SFINAE?

Автор: D_KEY 05.07.15, 23:29
Qraizer, думаю, что имеются в виду любые объекты-функции.

Автор: DarkEld3r 06.07.15, 06:36
Цитата Qraizer @
Выбирай костыль:
Так вот именно, что костыль. :)

Я ж не утверждал, что сделать нельзя и тем более того, что оно часто/сильно надо. Просто в расте синтаксически оно практически одинаково. Как аналог std::function для нешаблонных функций, так и дженерики.

Автор: Qraizer 06.07.15, 08:00
Тогда ждём концептов, которые дадут нам явные трейты вместо утиных. А до тех пор -- костылить их руками.

Автор: D_KEY 06.07.15, 08:02
Цитата DarkEld3r @
Цитата Qraizer @
Выбирай костыль:
Так вот именно, что костыль. :)

Я ж не утверждал, что сделать нельзя и тем более того, что оно часто/сильно надо. Просто в расте синтаксически оно практически одинаково. Как аналог std::function для нешаблонных функций, так и дженерики.

А эти костыли точно делают то, что ты хотел?

Автор: Qraizer 06.07.15, 08:03
D_KEY, если любых, то std::function<> само напрашивается, и это даже не костыль.

Автор: D_KEY 06.07.15, 08:22
Но std::function даст оверхед в рантайме. А было бы неплохо иметь возможность указать сигнатуру для шаблонного параметра без каких-либо оберток в рантайме. Думаю, что это возможно. Но смотреться будет страшно. Хотя может достаточно будет прочекать возможность создания std::function с нужной сигнатурой из объекта данного типа.

Автор: Qraizer 06.07.15, 09:43
С концептами будет возможно. Я надеюсь, иначе какие же это тогда концепты будут.

Добавлено
Вообще говоря, я подозреваю, что вы оба неправильно называете подразумеваемую вами сущность. Шаблонный параметр в данном случае не нуждается в специальных проверках. И вообще, сопоставление по образцу, реализуемое частичной специализацией, решает практически все подобные "концептуальные" проблемы. Вероятно, вы имели в виду параметры шаблонных функций. Это совершенно другой случай. Для шаблонных функций частичная специализация недоступна, вместо неё получается перегрузка. И подобным образом оформленное сопоставление с образцом вызывает иной механизм: разрешение перегрузки вместо выбора специализации. Работающий на иной стадии разбора сырцов к тому же. И решена поднятая проблема может быть только на стадии линковки в лучшем случае.

Автор: applegame 06.07.15, 10:09
Тут D_KEY приводил ссылку на бессмысленную задачку о compile-time проверки длины векторов с длиной задаваемой в run-time, в которой тролль-хаскелист обвинял C++ в отсутствии полноценного параметрического полиморфизма.
В той задачке суть свелась, к тому, что некий рекурсивный шаблон разворачивается в C++ в бесконечную рекурсию, так как ограничение этой рекурсии было уже в рантайме, что естественно в плюсах не сработает. Но аналогичный дженерик в Java и C# компилируются успешно.
D_KEY полагает, что это такое свойство дженериков. Типа вот, чем дженерики отличаются от шаблонов. А по-моему - это просто тонкости реализации. В жабе от бесконечной рекурсии спасает type erasure, в шарпе стирания типов нет, но инстанцирование шаблонов происходит в рантайме. В Хаскеле, по-видимому, все работает из-за повальной ленивости.
Вопрос, как реализованы дженерики в Rust и будет ли там компилироваться что-то вроде такого:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template<int N>
    int test(int b) {
        if(b == 0) {
            return 0;
        } else {
            return test<N + 1>(b - 1);
        }
    }
     
    int main() {
        test<0>(5);
    }

Автор: Qraizer 06.07.15, 10:26
applegame, там не задачка бессмысленна, а её постановка. Нельзя в compile-time что-либо сделать с сущностями, чьи свойства определяются в run-time, можно только создать алгоритм этого чего-то. С якобы compile-time алгоритмом его не Плюсовые примеры справляются сугубо из-за подмены понятий. А с определением алгоритма шаблоны справляются куда лучше, чем дженерики, только задачу для них найти надо. Типа шаблонов выражений.

Автор: applegame 06.07.15, 10:43
Цитата Qraizer @
applegame, там не задачка бессмысленна, а её постановка.
По мне так, задача с бессмысленной постановкой - бессмыслена.

Автор: D_KEY 06.07.15, 10:57
Цитата Qraizer @
Нельзя в compile-time что-либо сделать с сущностями, чьи свойства определяются в run-time, можно только создать алгоритм этого чего-то.

А с ними ничего и не делается. Доказывается утверждение, что у двух векторов/списков будет одна и та же длина. А конкретное значение длины, естественно, неизвестно.

Добавлено
Цитата applegame @
Тут D_KEY приводил ссылку на бессмысленную задачку о compile-time проверки длины векторов с длиной задаваемой в run-time, в которой тролль-хаскелист обвинял C++ в отсутствии полноценного параметрического полиморфизма.

Все дело в том, что шаблоны генерируют код. Сам код не является параметрически-полиморфным.

Цитата
D_KEY полагает, что это такое свойство дженериков. Типа вот, чем дженерики отличаются от шаблонов. А по-моему - это просто тонкости реализации.

Тут принципиальная разница как раз в том, что в C++(и в D?) у нас нет полиморфных типов/функций, у нас есть механизм генерации конкретных типов и функций в зависимости от параметров.
И это разница не в реализации.

Автор: Qraizer 06.07.15, 12:53
Цитата D_KEY @
Доказывается утверждение, что у двух векторов/списков будет одна и та же длина. А конкретное значение длины, естественно, неизвестно.
Не доказывается, а создаётся алгоритм доказательства. Тот факт, что C# умеет в run-time делать докомпиляцию, не меняет сути тезиса о том, что понятия подменены.
Цитата D_KEY @
Все дело в том, что шаблоны генерируют код. Сам код не является параметрически-полиморфным.
Забыл добавить "исходный" к "код". Шаблон не существует после компиляции единицы трансляции ...даже не так... после конкретизации в точке инстанцирования. Всё, в этой точке его больше нет. Вот в чём отличие шарповых дженериков. Шаблоны ровно такими свойствами и наделялись, чтобы не быть адовыми дженериками. Обвинять шаблоны в невлиянии на run-time всё равно, что Джаву в наличии виртуальной машины. Или std::vector<> в копировании свойств C-массива. Шаблонный код является абсолютно полным параметрически-полиморфным, если применять те же термины к тем же фазам жизненных циклов. Автор шаблонного кода наделяет его полиморфным поведением, которое доопределяется в точке использования. Всем бобра.

Автор: applegame 06.07.15, 12:55
Цитата D_KEY @
Все дело в том, что шаблоны генерируют код. Сам код не является параметрически-полиморфным.
Цитата D_KEY @
Тут принципиальная разница как раз в том, что в C++(и в D?) у нас нет полиморфных типов/функций, у нас есть механизм генерации конкретных типов и функций в зависимости от параметров.
И это разница не в реализации.

Не согласен. На самом деле парметрический полиморфизм существует только в головах программистов и в текстовом редакторе с искходником функции.
А по сути любой параметрический полиморфизм конвертируется в итоге в ad-hoc полиморфизм. Не думаешь же ты, что для работы, например c int и float применяется один и тот же машкод? Будь-то хваленный Хаскель или Java. Просто в C++ это явно прописано, а в всяких хаскелях спрятано под капотом.

Добавлено
Цитата Qraizer @
Шаблонный код является абсолютно полным параметрически-полиморфным, если применять те же термины к тем же фазам жизненных циклов. Автор шаблонного кода наделяет его полиморфным поведением, которое доопределяется в точке использования. Всем бобра.
Полностью согласен!

Автор: MyNameIsIgor 06.07.15, 13:30
Цитата Qraizer @
Не доказывается, а создаётся алгоритм доказательства. Тот факт, что C# умеет в run-time делать докомпиляцию, не меняет сути тезиса о том, что понятия подменены.

Я тут даже не знаю какой смайлик подобрать этой цитате: :lool: :facepalm: :fool:. Или все сразу? D_KEY, как думаешь?
Цитата Qraizer @
Забыл добавить "исходный" к "код". Шаблон не существует после компиляции единицы трансляции ...даже не так... после конкретизации в точке инстанцирования. Всё, в этой точке его больше нет. Вот в чём отличие шарповых дженериков.

Именно, вот в чём отличие обобщений - они не генерируют новые сущности. Шаблоны генерируют. Этот факт и позволяет сомневаться в том, что они являются реализацией параметрического полиморфизма.
Цитата applegame @
Не согласен. На самом деле парметрический полиморфизм существует только в головах программистов и в текстовом редакторе с искходником функции.

Т.е. там же, где и типы данных. А между тем Agda статически доказывает, что в программе нет деления на ноль, а в мейнстриме всё ещё неопределённое поведение и null reference exception. Так может быть среда существования чего-то ничего не говорит об этом чём-то?
Цитата applegame @
А по сути любой параметрический полиморфизм конвертируется в итоге в ad-hoc полиморфизм. Не думаешь же ты, что для работы, например c int и float применяется один и тот же машкод?

Как он там в реализации - абсолютно не важно. Потому что пример нагружает систему типов.
А так в C# обобщения реализованы вполне себе через динамический полиморфизм. И только если CLR захочет, может с'jit'ить специальный.

Добавлено
Цитата D_KEY @
Тут принципиальная разница как раз в том, что в C++(и в D?) у нас нет полиморфных типов/функций, у нас есть механизм генерации конкретных типов и функций в зависимости от параметров.

Дело осталось за малым: доказать, что лучше иметь правильный параметрический полиморфизм, чем генерацию сущностей.

Автор: D_KEY 06.07.15, 13:40
Цитата Qraizer @
Не доказывается, а создаётся алгоритм доказательства.

Смысл в том, что система типов поможет нам гарантировать, что длина будет одной и той же. Называй как хочешь.

Цитата
Тот факт, что C# умеет в run-time делать докомпиляцию, не меняет сути тезиса о том, что понятия подменены.

Эм. При чем тут какая-то докомпиляция?

Цитата
Забыл добавить "исходный" к "код". Шаблон не существует после компиляции единицы трансляции ...даже не так... после конкретизации в точке инстанцирования. Всё, в этой точке его больше нет. Вот в чём отличие шарповых дженериков.

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

Цитата
Шаблонный код является абсолютно полным параметрически-полиморфным, если применять те же термины к тем же фазам жизненных циклов.

Шаблонный код не полиморфен уже на уровне системы типов. Вот в чем проблема(для данного примера).

Добавлено
Цитата applegame @
Не согласен. На самом деле парметрический полиморфизм существует только в головах программистов и в текстовом редакторе с искходником функции.

Он существует в системе типов. Или не существует(как в С++).

Цитата
Не думаешь же ты, что для работы, например c int и float применяется один и тот же машкод? Будь-то хваленный Хаскель или Java. Просто в C++ это явно прописано, а в всяких хаскелях спрятано под капотом.

Вот у них как раз это дело реализации, а в C++ это делается в самом языке.

Добавлено
Цитата MyNameIsIgor @
Цитата D_KEY @
Тут принципиальная разница как раз в том, что в C++(и в D?) у нас нет полиморфных типов/функций, у нас есть механизм генерации конкретных типов и функций в зависимости от параметров.

Дело осталось за малым: доказать, что лучше иметь правильный параметрический полиморфизм, чем генерацию сущностей.

Не думаю, что это можно доказать. Это вопрос цены и достоинств того или другого подхода. В идеале хотелось бы иметь выбор, но его сложно представить себе в рамках одного языка.

Автор: MyNameIsIgor 06.07.15, 13:55
Цитата D_KEY @
Не думаю, что это можно доказать. Это вопрос цены и достоинств того или другого подхода.

А по-моему, это вопрос применения инструмента. Ибо в обсуждаемом примере параметрический полиморфизм применяется для имитации зависимых типов. Получается то лажа на самом деле.
Цитата D_KEY @
В идеале хотелось бы иметь выбор, но его сложно представить себе в рамках одного языка.

Так вот Rust же. Ну, или я чего-то не знаю, но в любом случае они очень близко подошли.

Автор: korvin 06.07.15, 13:59
Цитата applegame @
На самом деле парметрический полиморфизм существует только в головах программистов и в текстовом редакторе с исходником функции.

Даже более того, процедуры, циклы, типы тоже существуют только в головах программистов и в текстовом редакторе с исходником программы! All Hail Machine code!

Автор: applegame 06.07.15, 14:10
Цитата D_KEY @
Шаблонный код не полиморфен уже на уровне системы типов.
Я не понимаю, что значит не полиморфен на уровне системы типов? Вот тебе код:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template<typename T> foo(T param)
Ты хочешь сказать, что T - это не плиморфный тип?
Цитата D_KEY @
Вот у них как раз это дело реализации, а в C++ это делается в самом языке.
Нет, не делается. На языке C++ код вполне себе один, а то что компилятор нагенерит для каждого типа свои функции - это как раз детали реализации.
Цитата D_KEY @
Эм. При чем тут какая-то докомпиляция?
Потому что шарп тоже генерит для каждого типа свой код как и плюсы. Только шарп это делает run-time, а плюсы compile-time.
Цитата MyNameIsIgor @
Так вот Rust же. Ну, или я чего-то не знаю, но в любом случае они очень близко подошли.
И в Rust такая вот фигня скомпилится?
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template<int N>
    int test(int b) {
      if(b == 0) {
        return 0;
      } else {
        return test<N + 1>(b - 1);
      }
    }
        
    int main() {
       test<0>(5);
    }

Автор: D_KEY 06.07.15, 14:12
Цитата applegame @
Я не понимаю, что значит не полиморфен на уровне системы типов? Вот тебе код:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template<typename T> foo(T param)
Ты хочешь сказать, что T - это не плиморфный тип?

Эм... Конечно не полиморфный. А foo - неполиморфная функция, потому, что это вообще не функция.

Автор: applegame 06.07.15, 14:13
Цитата D_KEY @
Эм... Конечно не полиморфный. А foo - неполиморфная функция, потому, что это вообще не функция.
Ну и назови мне хоть одно отличие от "настоящей" функции.

Автор: D_KEY 06.07.15, 14:14
Цитата applegame @
На языке C++ код вполне себе один

Код может и один. Но std::vector<> - не является типом, а java.util.Vector<> - является.

Добавлено
Цитата applegame @
Потому что шарп тоже генерит для каждого типа свой код как и плюсы.

Шарп может генерировать, а может не генерировать - это деталь реализации. С++ не может не генерировать.

Цитата
И в Rust такая вот фигня скомпилится?
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template<int N>
    int test(int b) {
      if(b == 0) {
        return 0;
      } else {
        return test<N + 1>(b - 1);
      }
    }
        
    int main() {
       test<0>(5);
    }

Я вообще не уверен, что в Rust можно числа пихать в качестве параметров.

Добавлено
Цитата applegame @
Цитата D_KEY @
Эм... Конечно не полиморфный. А foo - неполиморфная функция, потому, что это вообще не функция.
Ну и назови мне хоть одно отличие от "настоящей" функции.

Для начала назови общее. А то я не очень понимаю, как мне сравнить шаблон функции с функцией.

Автор: applegame 06.07.15, 14:22
Цитата D_KEY @
Код может и один. Но std::vector<> - не является типом, а java.util.Vector<> - является.
Опять же не согласен. Конечно обычные переменные в C++ не могут иметь "тип" std::vector<>, а вот полиморфные в шаблонах могут. Особенно это заметно в D:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void foo(T: Vector!E, E)(T vector)

Очень хорошо видно, что T имеет тип Vector!E
Цитата D_KEY @
Шарп может генерировать, а может не генерировать - это деталь реализации. С++ не может не генерировать.
И то и другое - деталь реализации. Со своими недостатками и преимуществами.

Добавлено
Цитата D_KEY @
Для начала назови общее. А то я не очень понимаю, как мне сравнить шаблон функции с функцией.
Да элементарно. Я могу вызвать эту функцию, причем с параметрами разных типов. И функция ровно одна. Что там внутри сделает компилятор - детали реализации.

Добавлено
Цитата applegame @
Очень хорошо видно, что T имеет тип Vector!E
Даже еще проще:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void foo(E)(Vector!E vector);
Разве vector - не полиморфный параметр? Полиморфный, так как вектор может содержать элементы разных типов.

Добавлено
Неправильно выразился не T имеет тип, T - это и есть Vector!E

Автор: MyNameIsIgor 06.07.15, 14:32
Цитата applegame @
И то и другое - деталь реализации.

Какая деталь реализации? Стандарт C++ требует генерировать новые сущности, C# - не требует. При чём тут реализация?
Реализация - это способ выполнить требования. Таким образом в C++ нет способа выполнить требование генерации сущностей без генерации оных - значит, не деталь реализации. А в C# есть способ (через динамический полиморфизм) выполнить требования к обобщениям без генерации сущностей - значит, чего мы там генерируем является деталью реализации, не относящейся к сути вопроса.

Добавлено
Цитата applegame @
Да элементарно. Я могу вызвать эту функцию, причем с параметрами разных типов. И функция ровно одна. Что там внутри сделает компилятор - детали реализации.

Я смотрю, вы так и не поняли, почему мой тогдашний пример с обработкой аргументов variadic template не был рекурсивным.

Автор: applegame 06.07.15, 14:37
Цитата MyNameIsIgor @
Стандарт C++ требует генерировать новые сущности, C# - не требует.
И как это противоречит определению параметрического полиморфизма? Типа если в стандарте языка указано как именно компилятор должен реализовать полиморфизм - то это уже и не полиморфизм?
Цитата MyNameIsIgor @
Реализация - это способ выполнить требования. Таким образом в C++ нет способа выполнить требование генерации сущностей без генерации оных - значит, не деталь реализации.
Интересно, а хоть в каком-то определении параметрического полиморфизма есть оговорка, что не должно быть генерации сущностей? Крайне сомневаюсь.

Добавлено
Цитата MyNameIsIgor @
Я смотрю, вы так и не поняли, почему мой тогдашний пример с обработкой аргументов variadic template не был рекурсивным.
Я его, честно говоря и не видел. Я полез на ЛОР и уже оттуда не вылезал.

Автор: korvin 06.07.15, 14:48
В C++ же нельзя запихнуть шаблонную функцию в файл реализации, выставив в хэдер только сигнатуру? А как с этим в D?

Автор: MyNameIsIgor 06.07.15, 14:51
Цитата applegame @
Интересно, а хоть в каком-то определении параметрического полиморфизма есть оговорка, что не должно быть генерации сущностей? Крайне сомневаюсь.

Поэтому я и говорю
Цитата MyNameIsIgor @
позволяет сомневаться

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

Автор: applegame 06.07.15, 15:09
Цитата korvin @
В C++ же нельзя запихнуть шаблонную функцию в файл реализации, выставив в хэдер только сигнатуру? А как с этим в D?
В этом плане D ничем не отличается от C++. И поэтому в D мы также
Цитата MyNameIsIgor @
не можем на их основе доказывать те утверждения относительно типов, которые требуют бесконечного инстанцирования.

Автор: D_KEY 06.07.15, 15:14
Цитата applegame @
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void foo(T: Vector!E, E)(T vector)

Очень хорошо видно, что T имеет тип Vector!E

Блин. Vector!E - это конкретный тип, а не полиморфный тип Vector параметризированный E. Не знаю уже, как формулировать.

Цитата
Цитата D_KEY @
Шарп может генерировать, а может не генерировать - это деталь реализации. С++ не может не генерировать.
И то и другое - деталь реализации.

Нет. C# может не генерировать код для разных инстансов. И быть C#. Конкретная реализация С++ не может отказаться генерировать код для разных инстансов. Этого требует спецификация.

Добавлено
Цитата applegame @
Да элементарно. Я могу вызвать эту функцию

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

Цитата
И функция ровно одна.

Нет, их будет много. Или ты отрицаешь инстанцирование?

Автор: applegame 06.07.15, 15:19
Цитата D_KEY @
Блин. Vector!E - это конкретный тип, а не полиморфный тип Vector параметризированный E. Не знаю уже, как формулировать.
Какой же он конкретный? Это полиморфный тип. Это может быть и Vector!int и Vector!string и даже Vector!(Vector!string). Чем не полиморфность?
Цитата D_KEY @
Нет. C# может не генерировать код для разных инстансов. И быть C#. Конкретная реализация С++ не может отказаться генерировать код для разных инстансов. Этого требует спецификация.
Ну и какая разница? Нигде в определениях параметрического полиморфизма нет требований не "генерировать код для разных инстансов". Почему ты на основании этого утверждаешь, что в C++ полиморфизм не параметрический? Да, с бесконечными инстанцированиями есть проблема, но это, ИМХО, недостаток реализации полиморфизма в C++. И да, этот недостаток заложен в стандарт C++. И что с того?

Добавлено
Цитата D_KEY @
Не можешь ты ее вызвать. Потому, что сначала от компилятора требуется сгенерировать некоторую функцию по этому шаблону под твои параметры. А уже потом ты вызовешь эту сгенеренную функцию.
Нет я не вызываю никаких сгенеренных функций. Это уже компилятор формирует вызов нужной функции. А я как программист вызываю именно одну функцию.
Цитата D_KEY @
Нет, их будет много. Или ты отрицаешь инстанцирование?
Да мне-то какая разница как там оно будет внутри? И в Хаскеле их внутри будет много и в шарпе. Кому какое дело до того, что там будет внутри? У меня в коде одна функция? - одна. Что еще нужно-то?

Автор: DarkEld3r 06.07.15, 16:04
Цитата D_KEY @
А эти костыли точно делают то, что ты хотел?
Нет, но по моему, тема себя исчерпала - эта "задача" (если не расширять её до концептов) не сильно востребована на практике.

Цитата applegame @
и будет ли там компилироваться что-то вроде такого:
Не будет банально из-за того, что параметрами для дженериков не могут быть значения.

Автор: D_KEY 06.07.15, 16:24
Цитата applegame @
Это может быть и Vector!int и Vector!string и даже Vector!(Vector!string).

Это все будут отдельные конкретные типы. В этом и есть разница. Параметрический полиморфизм на уровне типов вводит эти самые полиморфные типы. А в C++ параметрический полиморфизм на уровне кода(и в D тоже, как я понял), то есть мы описываем некоторый шаблон кода с параметрами, а язык нам обещает генерацию уже конкретного типа на основе этого шаблона.


Цитата
Цитата D_KEY @
Не можешь ты ее вызвать. Потому, что сначала от компилятора требуется сгенерировать некоторую функцию по этому шаблону под твои параметры. А уже потом ты вызовешь эту сгенеренную функцию.
Нет я не вызываю никаких сгенеренных функций. Это уже компилятор формирует вызов нужной функции. А я как программист вызываю именно одну функцию.
Цитата D_KEY @
Нет, их будет много. Или ты отрицаешь инстанцирование?
Да мне-то какая разница как там оно будет внутри?

Такая, что система типов ничего не знает о твоих шаблонах. Вообще. Она умеет работать только с конкретными типами. А в Haskell том же в системе типов есть понятие полиморфных типов.

Добавлено
Цитата applegame @
И в Хаскеле их внутри будет много и в шарпе.

Не будет. Их будет много(возможно) в сгенеренном коде. Но в системе типов будет ровно один тип. И ровно одна функция. Тайпчекер работает с парметрами полиморфного типа практически как с типовыми переменными. В общем, объяснять долго, а толку будет мало :)

Автор: korvin 06.07.15, 16:33
Цитата applegame @
Какой же он конкретный? Это полиморфный тип. Это может быть и Vector!int и Vector!string и даже Vector!(Vector!string). Чем не полиморфность?

Тем, что это почти просто текстовая подстановка, почти как макросы (что Сишные, что Лисповые) или HTML-шаблоны, не более того.

С тем же успехом можно PHP называть "полиморфным HTML" или как-то так.

Автор: D_KEY 06.07.15, 16:39
Цитата korvin @
это почти просто текстовая подстановка

Ну это тоже неверно. "Подстановка кода" - более точное описание. Да и "шаблон кода", "шаблон типа", "шаблон функции" - подходящие названия.

Автор: korvin 06.07.15, 18:23
Цитата D_KEY @
Ну это тоже неверно. "Подстановка кода" - более точное описание. Да и "шаблон кода", "шаблон типа", "шаблон функции" - подходящие названия.

Ну, я не претендовал на абсолютную точность. =)

Впрочем, ИМХО, "подстановка кода" тоже не совсем верно, ведь нет никаких средств в compile-time инспектировать этот код, его "кодовость" заключается лишь в том, что его текст(собственно образца) является C++-кодом, а не любым произвольным текстом, как в Сишных макросах.

Кстати, не знаю, упоминалось ли тут (в разделе вообще), но в D термин mixin имеет два значения: один обще принятый, а второй --- как раз что-то вроде текстовых подстановок (чуть более продвинутых, чем Сишный препроцессор, но менее продвинутых, чем лисповые макросы).

Автор: amk 06.07.15, 18:53
Цитата korvin @
не любым произвольным текстом, как в Сишных макросах.
В Сишных макросах тоже не любой произвольный текст можно использовать. Есть ограничения.

Автор: korvin 06.07.15, 19:14
Цитата amk @
В Сишных макросах тоже не любой произвольный текст можно использовать. Есть ограничения.

Оу, не знал. Но там всё же "посвободней", чем в шаблонах?

Автор: amk 06.07.15, 19:35
Достаточно, чтобы строка через лексический анализатор проходила без ошибок.

Автор: applegame 06.07.15, 19:53
Цитата D_KEY @
Параметрический полиморфизм на уровне типов вводит эти самые полиморфные типы.
Это ты сам придумал такое определение? Я вот в педивикии не вижу такого обязательного требования:
Цитата
Параметрический полиморфизм позволяет определять функцию или тип данных обобщённо, так что значения обрабатываются идентично вне зависимости от их типа. Параметрически полиморфная функция использует аргументы на основе поведения, а не значения, апеллируя лишь к необходимым ей свойствам аргументов, что делает её применимой в любом контексте, где тип объекта удовлетворяет заданным требованиям поведения. Таким образом, реализуется концепция параметричности
Я не вижу причин несоответсвия плюсовых шаблонов этому определению. Может есть какое-то иное определение? Дай ссылку.
Цитата D_KEY @
Не будет. Их будет много(возможно) в сгенеренном коде. Но в системе типов будет ровно один тип. И ровно одна функция. Тайпчекер работает с парметрами полиморфного типа практически как с типовыми переменными. В общем, объяснять долго, а толку будет мало :)
Зачем объяснять? Я в целом понимаю, чем отличается система типов Хаскеля и плюсов. Я не понимаю, почему ты считаещь это различие достаточным для того чтобы утверждать, что шаблонные функции C++ не полиморфны параметрически.
У нас разногласия фактически на уровне терминологии. Я считаю, что такую вот "имитацию" можно считать параметрическим полиморфизмом.
Цитата korvin @
Тем, что это почти просто текстовая подстановка, почти как макросы (что Сишные, что Лисповые) или HTML-шаблоны, не более того.
Нэть, шаблоны обрабатываются компилятором, а не препроцессором, со всеми вытекающими. Разница огромна.
Цитата korvin @
С тем же успехом можно PHP называть "полиморфным HTML" или как-то так.
Ну вообще-то, в похапе любая функция полиморфна по всем параметрам.

Добавлено
Цитата korvin @
Кстати, не знаю, упоминалось ли тут (в разделе вообще), но в D термин mixin имеет два значения: один обще принятый, а второй --- как раз что-то вроде текстовых подстановок (чуть более продвинутых, чем Сишный препроцессор, но менее продвинутых, чем лисповые макросы).
Я упоминал в соответствующем холиворе. Но народ не оценил. Дескать в Nemerle макросы круче. Не спорю, там (в Nemerle) красота, но в целом возможность любую текстовую строку компильнуть как D-шный код, в купе с произвольным CTFE - весьма приятная штука.

Автор: korvin 06.07.15, 19:58
Цитата applegame @
Нэть, шаблоны обрабатываются компилятором, а не препроцессором, со всеми вытекающими. Разница огромна.

Ну вообще-то, в похапе любая функция полиморфна по всем параметрам.

Лисповые макросы тоже обрабатываются компилятором. Так в чём огромность разницы?

Да не совсем, для динамической типизации все эти рассуждения довольно эфемерны. Более того, там всё универсально-полиморфно, а не параметрически, т.к. последний имеет смысл только при статической типизации.

Автор: Qraizer 06.07.15, 20:02
Цитата D_KEY @
Смысл в том, что система типов поможет нам гарантировать, что длина будет одной и той же. Называй как хочешь.
Я не против сравнения дженериков и шаблонов, D_KEY, я против того, что те и другие были поставлены в равные условия, коли речь в постановке задачи касается терминов "compile-time" и "run-time". Из-за неопытности (?) автора получилось нечто подобное сравнению полнофункционального std::vector<> и динамического C-массива с нереализованным контролем стораджа. Естественно std::vector<> проиграет, и либо вместо него взять следовало std::array<>, либо дореализовать фичу перераспределения стораджа для C-массива.
Цитата D_KEY @
При чем тут какая-то докомпиляция?
Объясняю на пальцах. Дженерики инстанцируются в run-time, при этом получается новый байт-код. Если есть jit-компиляция, то она будет произведена, а значит термин "run-time" уже неполно характеризует происходящие в нём процессы, т.к. подразумевает "compile-time" уже во время "run-time"; если же jit-а нет, то термин "compile-time" вообще неприменим в данном случае, ибо новый байт-код будет интерпретироваться.
С тем же успехом можно на Плюсах реализовать – правда, придётся это делать руками, но никто не мешает на будущее запилить сие себе в библиотеку – досоздание исполняемого объектного кода, и C# тут отнюдь не окажется в выигрыше. Или попробовать сравнить C# и банальным GW-Basic, где последний заткнёт его за пояс однозначно.
Цитата D_KEY @
Шаблонный код не полиморфен уже на уровне системы типов. Вот в чем проблема(для данного примера).
D_KEY, ты меня удивляешь. Полиморфизм не имеет никакого отношения к типам. Он имеет отношение к поведению. Впрочем, я догадываюсь, каким будет следующее возражение.

Автор: applegame 06.07.15, 20:07
Цитата korvin @
Лисповые макросы тоже обрабатываются компилятором. Так в чём огромность разницы?
С Лиспом не знаю. Я про сишные макросы.
Цитата korvin @
а не параметрически, т.к. последний имеет смысл только при статической типизации.
Ты уверен? Пруф можно? Что-то я не встречал такого странного условия для параметрического полиморфизма - статическая типизация.

Добавлено
Предлягаю вынести спор о плюсовом полиморфизме в отдельный холивор :)

Автор: korvin 06.07.15, 21:44
Цитата applegame @
Ты уверен? Пруф можно? Что-то я не встречал такого странного условия для параметрического полиморфизма - статическая типизация.

Я уверен на 100%. Параметрический полиморфизм(ПП) параметризуется типами, что совершенно бессмысленно при динамической типизации, т.к. там в любом случае типы приходится выяснять в рантайме и то только если нужно. Фактически ПП нужен для "обхода" ограничений примитивной статической типизации и написания обощённого кода. Попробуй написать функцию len, вычисляющую длину списка в Лиспе(да вообще в любом ЯП с дин. типизацией) и в D (вообще в любом ЯП со статической типизацией) и в (оригинальном)Паскале, например.

Автор: MyNameIsIgor 06.07.15, 22:12
Цитата korvin @
Попробуй написать функцию len, вычисляющую длину списка в Лиспе(да вообще в любом ЯП с дин. типизацией) и в D (вообще в любом ЯП со статической типизацией) и в (оригинальном)Паскале, например.

Да чего уж так сложно то? :) Банальный жабоскрипт
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    function f(a, b) { return a+b; }

будет работать для кучи типов. В языках со статической типизацией возникнут проблемы.

Автор: Qraizer 06.07.15, 22:19
Цитата korvin @
В C++ же нельзя запихнуть шаблонную функцию в файл реализации, выставив в хэдер только сигнатуру?
Можно. Это называется экспортом шаблона. Существует с C++98. Тот факт, что реализован он только в одном-единственном компиляторе, лично я считаю сознательным саботажем со стороны поставщиков реализации. С C++11 по этой причине экспорт шаблонов не является обязательным.
Однако положа руку на сердце, я признаю, что работать с шаблоном, не имея его сырцов, в условиях отсутствия концептов при отладке своего можно получить геморр ещё тот.

Автор: D_KEY 06.07.15, 22:27
Цитата applegame @
Цитата D_KEY @
Параметрический полиморфизм на уровне типов вводит эти самые полиморфные типы.
Это ты сам придумал такое определение?

Это не определение. Я пытался объяснить разницу между параметрическим полиморфизмом уровня системы типов и параметрическим полиморфизмом на уровне кода(что мы наблюдаем в C++ и D).
Параметрический полиморфизм есть в C++(признаю таки), но там нет полиморфных типов. Уже не знаю, как еще сказать. И не думаю, что мы способны извлечь что-либо болезное из продолжения данного спора :)

Автор: Qraizer 06.07.15, 22:29
Цитата korvin @
Тем, что это почти просто текстовая подстановка, почти как макросы (что Сишные, что Лисповые) или HTML-шаблоны, не более того.
Давай не будем сызнова реинкарнировать ещё одну давным-давно избитую до смерти тему. У макросов с шаблонами примерно столько же общего, как у телефонистки и сотового: результат похож, но это и всё.

Автор: D_KEY 06.07.15, 22:37
Цитата Qraizer @
я против того, что те и другие были поставлены в равные условия, коли речь в постановке задачи касается терминов "compile-time" и "run-time".

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

Цитата
Дженерики инстанцируются в run-time, при этом получается новый байт-код.

Не обязательно. Они вообще могут не инстанцироваться и не генерировать байт-код во время исполнения. Если мы о дженериках.
Не знаю, как там в последних версиях, но JVM очень долго(а может и сейчас) ничего о дженериках не знала и компилятор там генерировал код для Object и обеспечивал boxing/unboxing для встроенных типов.

Цитата
С тем же успехом можно на Плюсах реализовать – правда, придётся это делать руками, но никто не мешает на будущее запилить сие себе в библиотеку – досоздание исполняемого объектного кода

Есть libjit, например. Если я правильно тебя понял. Но суть-то не в том, что там и когда мы генерируем. Вопрос в том, кто и как проверяет. Система типов C++ не знает про шаблоны, шаблоны сначала инстанцируются и сначала мы получаем тип, а потом уже осуществляется с ним работа. Потому тайпчекер не приступит к работе, пока мы не инстанцируем тип. В случае же полиморных типов инстанцировать тип нет необходимости(кроме случаев оптимизации).

Добавлено
Цитата MyNameIsIgor @
Цитата korvin @
Попробуй написать функцию len, вычисляющую длину списка в Лиспе(да вообще в любом ЯП с дин. типизацией) и в D (вообще в любом ЯП со статической типизацией) и в (оригинальном)Паскале, например.

Да чего уж так сложно то? :) Банальный жабоскрипт
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    function f(a, b) { return a+b; }

будет работать для кучи типов. В языках со статической типизацией возникнут проблемы.

Пример со списком более "чист" из-за отсутствия требований к типам. Списку все равно, что хранить.

Автор: Qraizer 06.07.15, 23:11
Не буду цитировать, мне сейчас крайне неудобно красиво оформлять посты, я в Крыму с ноутом жены. Просто пронумерую по порядку.
  1. Обсуждайте ради бога. Я не буду спорить касательно термина "полиморный тип", однако скажу своё мнение в 3-ем пункте. Я вошёл в спор с целью показать неверность вывода того хаскелиста (что кем он там являлся-то). Он взял некую сущность из одного языка и сравнил с похожей сущностью другого языка, проигнорировав тот факт, что эти сущности не просто разные, но и сферы их применения разные, и назначения тоже разные. Немудрено, что пришёл к неверному выводу.
  2. Так шаблоны тоже могут не инстанцироваться. И даже не полностью инстанцироаться при конкретизации. (А вот дженерики последнее могут?)
    Формально Object с боксингизмом, не особо передёргивая при этом, можно назвать аналогом интерпретации. Мне это напоминает старую добрую Class Library от Borland, которая вполне с успехом применялась в Borland C++ дошаблонных версий и предназначалась для того же, для чего сейчас используется STL. Да, приходилось любой тип сначала делать полиморфным, оборачивая в наследника TObject. Т.о. всё отличие в том, что теперь это делал шарповый компилятор, а не программер руками.
  3. Честно говоря, не знаю, правильно ли. Я "просто" предложил как вариант уравнивания шаблонов и дженериков донаделить C++ возможностью, которой шаблоны не обладают, но обладают дженерики: достраивать исполняемый код в run-time. Вот тогда действительно суть будет не в том, что и когда достраивается.
    Что касается полиморфных типов, то я вообще не вполне понимаю, что под этим вы подразумеваете. Лично я не знаю таких за пределами классов с виртуальными методами, каковые называются полиморфными просто для краткости. Тип с моей точки зрения является просто-напросто неким атрибутом сущности. И этот атрибут однозначно определяет свойства этой сущности, т.е. является просто удобным кратким описанием её характеристик. К термину "полиморфный тип" я отношусь примерно так же, как к "целочисленный" или там "32-битный". Т.е. он мне не более чем говорит о некой конкретной особенности этого типа.
    Если теперь вспомнить, для чего прилагательное "полиморфный" в контексте динамического полиморфизма добавляется к описанию классов с виртуальными методами, то с этой точки зрения любой тип в статическом полиморфизме является полиморфным. О чём тогда спор, я не знаю.

Автор: D_KEY 06.07.15, 23:22
Цитата Qraizer @
Я вошёл в спор с целью показать неверность вывода того хаскелиста (что кем он там являлся-то). Он взял некую сущность из одного языка и сравнил с похожей сущностью другого языка, проигнорировав тот факт, что эти сущности не просто разные, но и сферы их применения разные, и назначения тоже разные. Немудрено, что пришёл к неверному выводу.

Но вывод там в том, что на C++ такую проверку сделать нельзя. И ведь действительно нельзя.

Автор: applegame 07.07.15, 06:43
Цитата korvin @
уверен на 100%. Параметрический полиморфизм(ПП) параметризуется типами, что совершенно бессмысленно при динамической типизации, т.к. там в любом случае типы приходится выяснять в рантайме и то только если нужно. Фактически ПП нужен для "обхода" ограничений примитивной статической типизации и написания обощённого кода.
Я нигде и никогда не встречал такого странного понимания ПП (ну разве только что того Хаскелиста с ЛОРа). Кто-нибудь кроме тебя еще считает, что ПП это прерогатива языков исключительно со статической типизацией? Или это твоя личная интерпретация понятия ПП?
Цитата korvin @
Попробуй написать функцию len, вычисляющую длину списка в Лиспе(да вообще в любом ЯП с дин. типизацией) и в D (вообще в любом ЯП со статической типизацией) и в (оригинальном)Паскале, например.
Цитата MyNameIsIgor @

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    function f(a, b) { return a+b; }

будет работать для кучи типов. В языках со статической типизацией возникнут проблемы.
Не улавливаю хода вашей мысли. Поподробней пожалуйста.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    auto foo(A, B)(A a, B b) { return a + b; }
ЧЯДНТ?
Цитата D_KEY @
Я пытался объяснить разницу между параметрическим полиморфизмом уровня системы типов и параметрическим полиморфизмом на уровне кода(что мы наблюдаем в C++ и D).
Ну, эту разнице объяснять не нужно, она понятна. Я просто не согласен, что она является определяющей для понятия ПП. Korvin похоже считает иначе.
Цитата D_KEY @
Но вывод там в том, что на C++ такую проверку сделать нельзя. И ведь действительно нельзя.
Если бы там был такой вывод, то не было и того холивара. Там делается другой вывод: C++ - говно, C# и Java тоже говно, но менее вонючее, а Хаскель - няшка.

Автор: D_KEY 07.07.15, 08:22
Цитата applegame @
Кто-нибудь кроме тебя еще считает, что ПП это прерогатива языков исключительно со статической типизацией?

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

Автор: applegame 07.07.15, 08:34
Цитата D_KEY @
Я считаю, что в языках с динамической типизацией параметрический полиморфизм бессмысленнен.
Зачем он там нужен-то? Можешь пример привести?
Что значит бессмысленнен? Он там просто есть. Методы в Ruby всегда полиморфны по всем параметрам. И в жабаскрипте тоже. Еще раз читаем определение ПП, вдумчиво пожалуйста и ищем там хоть какое-то упоминание динамической/статической типизации, run-time или compile-time:
Цитата Педивикия
Параметрический полиморфизм позволяет определять функцию или тип данных обобщённо, так что значения обрабатываются идентично вне зависимости от их типа. Параметрически полиморфная функция использует аргументы на основе поведения, а не значения, апеллируя лишь к необходимым ей свойствам аргументов, что делает её применимой в любом контексте, где тип объекта удовлетворяет заданным требованиям поведения. Таким образом, реализуется концепция параметричности

Автор: D_KEY 07.07.15, 08:36
Цитата applegame @
Цитата korvin @
Попробуй написать функцию len, вычисляющую длину списка в Лиспе(да вообще в любом ЯП с дин. типизацией) и в D (вообще в любом ЯП со статической типизацией) и в (оригинальном)Паскале, например.
Цитата MyNameIsIgor @

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    function f(a, b) { return a+b; }

будет работать для кучи типов. В языках со статической типизацией возникнут проблемы.
Не улавливаю хода вашей мысли. Поподробней пожалуйста.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    auto foo(A, B)(A a, B b) { return a + b; }
ЧЯДНТ?

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

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

Автор: DarkEld3r 07.07.15, 08:48
Цитата D_KEY @
которые могут иметь совершенно любые типы, в том числе несовместимые для бинарной операции или вообще не поддерживающие данную операцию. И определяется это все только во время выполнения.
И чем это хорошо?

Цитата D_KEY @
В некоторых других языках со статической типизацией ты бы использовал дженерик-функцию с ограничением(для вызова +)
Вариант на расте:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    fn f<T1, T2, R>(a: T1, b: T2) -> R
        where T1: std::ops::Add<T2, Output = R> {
        a + b
    }


Цитата Qraizer @
С C++11 по этой причине экспорт шаблонов не является обязательным.
А разве не убрали совсем?

Цитата Qraizer @
И даже не полностью инстанцироаться при конкретизации. (А вот дженерики последнее могут?)
В расте, в данный момент, вообще нет специализации. :(

Автор: applegame 07.07.15, 09:34
Цитата D_KEY @
В языках с динамической типизацией в этом нет никакой необходимости, так как код изначально принимает объекты, которые могут иметь совершенно любые типы, в том числе несовместимые для бинарной операции или вообще не поддерживающие данную операцию. И определяется это все только во время выполнения.
Именно так. И именно поэтому все функции в динамически типизированных языках всегда параметрически полиморфны. А в C++/D/Rust/Java/C# и т.п. для таких функций создан специальный синтаксис.

Автор: D_KEY 07.07.15, 09:35
Цитата DarkEld3r @
И чем это хорошо?

Некоторым нравится. Я сторонник статической типизации.

Добавлено
Цитата applegame @
Именно так. И именно поэтому все функции в динамически типизированных языках всегда параметрически полиморфны.

Ты, конечно, можешь так считать. Но это довольно бессмысленное утверждение.
Цитата wiki
Parametric polymorphism - In programming languages and type theory, parametric polymorphism is a way to make a language more expressive, while still maintaining full static type-safety. Using parametric polymorphism, a function or a data type can be written generically so that it can handle values identically without depending on their type.[1] Such functions and data types are called generic functions and generic datatypes respectively and form the basis of generic programming.


Добавлено
Цитата applegame @
Ну, эту разнице объяснять не нужно, она понятна. Я просто не согласен, что она является определяющей для понятия ПП.

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

Цитата
Цитата D_KEY @
Но вывод там в том, что на C++ такую проверку сделать нельзя. И ведь действительно нельзя.
Если бы там был такой вывод, то не было и того холивара. Там делается другой вывод: C++ - говно, C# и Java тоже говно, но менее вонючее, а Хаскель - няшка.

Ну такого я там не нашел. По сути своим примером он показал, что в C++ есть генерация кода, но нет параметрического полиморфизма.

Автор: applegame 07.07.15, 09:54
Цитата D_KEY @
Ты, конечно, можешь так считать. Но это довольно бессмысленное утверждение.
Все ясно. Я понимаю ПП в более широком смысле, чем ты и korvin. Спор опять ведется вокруг терминов, которые четко нигде не определены. В любом случае, думаю, я понял ваше понимание ПП. Я с ним не согласен, но спор тут точно ничего не решит.

Добавлено
Цитата D_KEY @
Ну такого я там не нашел. По сути своим примером он показал, что в C++ есть генерация кода, но нет параметрического полиморфизма.
Нет, он показал, что шаблоны не справляются с некоторыми задачами, с которыми могут справится языки с более продвинутой системой типов, а не то что в C++ нет ПП. Попросту говоря сделал вывод, который не следует из показанного. А так как он это явно сделал намеренно, то можно просто сказать - солгал.

Автор: D_KEY 07.07.15, 09:58
Цитата applegame @
Нет, он показал, что шаблоны не справляются с некоторыми задачами, с которыми могут справится языки с более продвинутой системой типов, а не то что в C++ нет ПП.

Цитата
Some implementations of type polymorphism are superficially similar to parametric polymorphism while also introducing ad hoc aspects. One example is C++ template specialization.

Автор: applegame 07.07.15, 10:00
Цитата D_KEY @
Some implementations of type polymorphism are superficially similar to parametric polymorphism while also introducing ad hoc aspects. One example is C++ template specialization.
Причем тут специализация шаблонов? Да, специализация шаблонов - это явно не ПП, это явный ad-hoc полиморфизм. Но мы то не его обсуждаем.

Автор: D_KEY 07.07.15, 10:06
Цитата applegame @
Цитата D_KEY @
Some implementations of type polymorphism are superficially similar to parametric polymorphism while also introducing ad hoc aspects. One example is C++ template specialization.
Причем тут специализация шаблонов? Да, специализация шаблонов - это явно не ПП, это явный ad-hoc полиморфизм. Но мы то не его обсуждаем.

Мы обсуждаем, являются ли шаблоны параметрическим полиморфизмом.

Это парметрический полиморфизм?
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template<typename T>
    void foo(T a)
    {
        bar(a);
    }


А если добавить:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template<>
    void foo<int>(int x)
    {
    }

?
Параметрический полиморфизм вдруг испариться? Так может его и не было?

Автор: applegame 07.07.15, 10:26
Цитата D_KEY @
Мы обсуждаем, являются ли шаблоны параметрическим полиморфизмом.
Ты же уже признал, что является :D
Цитата D_KEY @
Параметрический полиморфизм есть в C++(признаю таки), но там нет полиморфных типов.

Цитата D_KEY @
Параметрический полиморфизм вдруг испариться? Так может его и не было?
Да, испарится. Перегрузка функций и специализация - ad-hoc полиморфизм.

Автор: D_KEY 07.07.15, 11:07
Цитата applegame @
Цитата D_KEY @
Мы обсуждаем, являются ли шаблоны параметрическим полиморфизмом.
Ты же уже признал, что является :D

Ну там был другой контекст, для которого нюансы были неважны.

Цитата
Цитата D_KEY @
Параметрический полиморфизм вдруг испариться? Так может его и не было?
Да, испарится. Перегрузка функций и специализация - ad-hoc полиморфизм.

Так получается, что вот тут:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template<typename T>
    void foo(T a)
    {
        bar(a);
    }

мы не можем сказать, есть ли тут параметрический полиморфизм. Ведь мы не знаем, будет ли специализация. Более того, мы не знаем, что за bar у нас тут :) Тут же будет учтена перегрузка, так? А ведь это не параметрический полиморфизм.

Автор: applegame 07.07.15, 11:14
Цитата D_KEY @
мы не можем сказать, есть ли тут параметрический полиморфизм.
Все зависит от того как эта функция будет определена в точке вызова/инстанцирования шаблона.
bar тут совсем не причем. Полиморфность функции bar никак не влияет на полиморфность foo.

Автор: MyNameIsIgor 07.07.15, 11:23
Цитата applegame @
Не улавливаю хода вашей мысли. Поподробней пожалуйста.

Нет никакого смысла рассуждать о параметрическом полиморфизме при динамической типизации. Собственно параметрический полиморфизм и был описан именно для статической типизации. Для динамических языков об этом просто никто не задумывался - это бессмысленно.

Автор: applegame 07.07.15, 11:36
Цитата MyNameIsIgor @
Нет никакого смысла рассуждать о параметрическом полиморфизме при динамической типизации. Собственно параметрический полиморфизм и был описан именно для статической типизации. Для динамических языков об этом просто никто не задумывался - это бессмысленно.
Рассуждать да, бессмысленно. Там все всегда полиморфно. Просто korvin зачем-то вспомнил про похапе.

Автор: D_KEY 07.07.15, 11:53
Цитата applegame @
Все зависит от того как эта функция будет определена в точке вызова/инстанцирования шаблона.

И именно поэтому это не параметрический полиморфизм
Цитата
Using parametric polymorphism, a function or a data type can be written generically so that it can handle values identically without depending on their type

Не должно быть зависимости от типа и зависимости от точки инстанцирования.

Цитата
bar тут совсем не причем. Полиморфность функции bar никак не влияет на полиморфность foo.

Чем докажешь?

Автор: Qraizer 07.07.15, 14:11
Цитата D_KEY @
Но вывод там в том, что на C++ такую проверку сделать нельзя. И ведь действительно нельзя.
Можно.

Добавлено
Цитата D_KEY @
Так получается, что вот тут:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template<typename T>
    void foo(T a)
    {
        bar(a);
    }

мы не можем сказать, есть ли тут параметрический полиморфизм. Ведь мы не знаем, будет ли специализация. Более того, мы не знаем, что за bar у нас тут :) Тут же будет учтена перегрузка, так? А ведь это не параметрический полиморфизм.

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    struct A
    {
      virtual void f() const { std::cout << "I'm A::f()" << std::endl; }
    };
     
    struct B: A
    {
      virtual void f() const { std::cout << "I'm B::f()" << std::endl; }
    };
     
    void foo(const A& a)
    {
      a.f();
      a.A::f();     // внезапно испаряющийся динамический полиморфизм
    }
     
    int main()
    {
      foo(B());
    }


Добавлено
В C++ нет динамического полиморфизма, потому что A не знает, будет ли кто-нибудь явно квалифицировать метод f() маршрутом к A. Всё правильно, D_KEY?

Добавлено
P.S. Перегрузка – это вообще мультиметоды, только статически полиморфные. За динамическими мультиметодами добро пожаловать в C++ Общие, я там темку создавал.

Автор: D_KEY 07.07.15, 14:49
Цитата Qraizer @
Цитата D_KEY @
Но вывод там в том, что на C++ такую проверку сделать нельзя. И ведь действительно нельзя.
Можно.

Покажешь?

Добавлено
Цитата Qraizer @
В C++ нет динамического полиморфизма, потому что A не знает, будет ли кто-нибудь явно квалифицировать метод f() маршрутом к A. Всё правильно, D_KEY?

Неверная аналогия.

Автор: Qraizer 07.07.15, 15:57
А что тут показывать? Проблема подсчитать длину списка? Или сравнить два числа? Это азы метакодирования.
Аналогия абсолютная. Полиморфизм сознательно выключен для конкретного случая. И вообще, специализации имеют весьма опосредованное отношение к полиморфизму, скорее тогда уж к его отмене. Замечу: это делает программист, и делает это сознательно, а по дефолту полиморфизм будет продолжать иметь место. К слову, явная квалификация отнюдь не обязана его отключить, можно привести примеры с множественным наследованием, когда квалификацией переключается маршрут по иерархии. В моих мультиметодах подобным образом Visitor восстанавливает динамический тип очередного позднесвязываемого параметра, только там для этого используется static_cast<>. Аналогично частичная специализация может не отменять статически полиморфное поведение, а переключить полиморфный алгоритм в другое направление.

Добавлено
P.S. Внезапно всплыл термин "полиморфный алгоритм". :o Готовить попкорн?

Автор: D_KEY 07.07.15, 16:07
Цитата Qraizer @
А что тут показывать? Проблема подсчитать длину списка? Или сравнить два числа? Это азы метакодирования.

Покажи решение данной задачи на C++.

Цитата
Аналогия абсолютная. Полиморфизм сознательно выключен для конкретного случая.

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

Цитата
И вообще, специализации имеют весьма опосредованное отношение к полиморфизму, скорее тогда уж к его отмене.

Цитата
Аналогично частичная специализация может не отменять статически полиморфное поведение, а переключить полиморфный алгоритм в другое направление.

Цитата
Замечу: это делает программист, и делает это сознательно, а по дефолту полиморфизм будет продолжать иметь место.

Я говорил не об отмене полиморфизма, я говорил об "отмене" параметрического полиморфизма(если таковой вообще был изначально). В параметрическом полиморфизме код не зависит от того, с какими данными мы работаем. А в C++ зависит. Из-за перегрузки функций и из-за специализации шаблонов.

Автор: Qraizer 07.07.15, 16:39
Цитата D_KEY @
Она выключена в разных местах. В твоем примере клиент выбирает, какой вызов ему делать.
В твоём тоже. Да и в принципе я могу и в struct A выключить, не проблема добавить A::g() с вызовом A::f() внутри.
D_KEY, перегрузка и специализации ортогональны полиморфизму. Их наличие или отсутствие конкретно где-то там никак не влияют на наличие или отсутствие полиморфизма вообще.

Автор: D_KEY 07.07.15, 17:33
Цитата Qraizer @
Их наличие или отсутствие конкретно где-то там никак не влияют на наличие или отсутствие полиморфизма вообще.

Цитата
Я говорил не об отмене полиморфизма, я говорил об "отмене" параметрического полиморфизма(если таковой вообще был изначально). В параметрическом полиморфизме код не зависит от того, с какими данными мы работаем. А в C++ зависит. Из-за перегрузки функций и из-за специализации шаблонов.


Добавлено
Qraizer, так будет решение задачи на плюсах?

Автор: applegame 07.07.15, 20:40
На лоре приводили решение.

Автор: Qraizer 07.07.15, 21:26
Знаешь, D_KEY, я сейчас разрываюсь между следующими вариантами:
  • D_KEY хочет 100500-ый раз посмотреть на 100499 раз виденные метасписки и метафункции;
  • D_KEY знает, что именно он увидит, и хочет ткнуть носом в решение не той задачи, несмотря на то, что это исходно другая задача, и разница между ними коллинеарна к обсуждаемому вопросу;
  • D_KEY хочет потроллить Qraizer-а тем, что у того нет на ноуте жены и коммуникаторе в роуминге никаких средств разработки, а через Крымский Wi-Fi этот пост он пытается отправить уже третий раз;
  • D_KEY хочет потролить Qraizer-а тем, что ideone что-то колбасит уже 6 часов.
Свой вариант?

Автор: MyNameIsIgor 07.07.15, 22:13
Цитата applegame @
На лоре приводили решение.

Там нет подходящего решения. Тем более после усложнения.
Цитата Qraizer @
Свой вариант?

Qraizer нихрена задачу не понял и потому думает, что может её решить. А на самом деле кишка тонка.

Автор: D_KEY 07.07.15, 22:51
Цитата applegame @
На лоре приводили решение.

Там вроде типы руками стирают? Это можно да(хотя... надо подумать). И что, это не проясняет для тебя мою позицию?

Qraizer какой-то другой вариант подразумевает.

Добавлено
Цитата Qraizer @
Знаешь, D_KEY, я сейчас разрываюсь между следующими вариантами:
  • D_KEY хочет 100500-ый раз посмотреть на 100499 раз виденные метасписки и метафункции;
  • D_KEY знает, что именно он увидит, и хочет ткнуть носом в решение не той задачи, несмотря на то, что это исходно другая задача, и разница между ними коллинеарна к обсуждаемому вопросу;

Так я тебе и рассказал Зависит от.

А со сроками я не торопил, можешь хоть через пару месяцев написать ;)

Автор: MyNameIsIgor 08.07.15, 00:01
Цитата D_KEY @
Там вроде типы руками стирают?

Да. Выше я тоже писал, что это должно помочь. Сейчас я передумал. Потому что стирание типа позволяет остановить инстанцирование, но и уничтожает тип, т.е. не позволяет во время компиляции определить, что списки одинаковой длины. На практике это сводится к тому, что если мы ошибёмся при создании обёртки из оригинальных списков, то компилятор это не поймает, а по задумке должен. Понимаешь меня?

Автор: applegame 08.07.15, 06:31
Цитата D_KEY @
И что, это не проясняет для тебя мою позицию?
Твоя позиция мне ясна.

Добавлено
Цитата MyNameIsIgor @
Да. Выше я тоже писал, что это должно помочь. Сейчас я передумал. Потому что стирание типа позволяет остановить инстанцирование, но и уничтожает тип, т.е. не позволяет во время компиляции определить, что списки одинаковой длины. На практике это сводится к тому, что если мы ошибёмся при создании обёртки из оригинальных списков, то компилятор это не поймает, а по задумке должен.
Система типов C++ недостаточно мощная, поэтому единственный путь - это сэмулировать более мощную систему типов, постулировав при этом абсолютную правильность этой эмуляции, то бишь обертки. Это конечно не совсем то, что требуется в исходной задаче, но если посмотреть шире, то ситуация похожая, так как в исходной задаче постулируется абсолютная правильность самого компилятора.

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

Вообще Хаскель очень красивый язык, но как кто-то точно выразился:
Цитата
Хаскель — это как ламборджини в деревне. Немного подрочил — и пошел работать на тракторе.

Автор: D_KEY 08.07.15, 07:51
Цитата MyNameIsIgor @
Цитата D_KEY @
Там вроде типы руками стирают?

Да. Выше я тоже писал, что это должно помочь. Сейчас я передумал. Потому что стирание типа позволяет остановить инстанцирование, но и уничтожает тип, т.е. не позволяет во время компиляции определить, что списки одинаковой длины. На практике это сводится к тому, что если мы ошибёмся при создании обёртки из оригинальных списков, то компилятор это не поймает, а по задумке должен. Понимаешь меня?

Да, понимаю. Похожие мысли заставили меня написать в скобочках, что надо подумать :)

Добавлено
applegame, не надо на haskell сворачивать. С данной задачей справляются и более слабые языки :)

Надо таки посмотреть, что rust скажет по этому поводу.

Автор: applegame 08.07.15, 09:10
Цитата D_KEY @
С данной задачей справляются и более слабые языки
Да. Но задача все же выеденного яйца не стоит. Очень уж исскуственная.

Автор: D_KEY 08.07.15, 09:17
Цитата applegame @
Цитата D_KEY @
С данной задачей справляются и более слабые языки
Да. Но задача все же выеденного яйца не стоит. Очень уж исскуственная.

Ну это просто пример. В принципе, тут можно обеспечить более крутые проверки.

А суть в том, что в C++ нет настоящего параметрического полиморфизма, лишь имитация посредством шаблонов(да, они во многом позволяют делать более крутые вещи, чем дженерики или полиморфные типы).

Автор: applegame 08.07.15, 09:20
Цитата D_KEY @
А суть в том, что в C++ нет настоящего параметрического полиморфизма
Как мы уже выяснили, это зависит от того, что понимать под понятием ПП. Я, лично считаю, что это самый что ни на есть настоящий параметрический полиморфизм.

Добавлено
Цитата D_KEY @
Ну это просто пример. В принципе, тут можно обеспечить более крутые проверки.
Тут только одно ограничение - рекурсивное инстанцирование. на этом более крутые проверки заканчиваются.

Автор: D_KEY 08.07.15, 09:22
Цитата applegame @
Как мы уже выяснили, это зависит от того, что понимать под понятием ПП. Я, лично считаю, что это самый что ни на есть настоящий параметрический полиморфизм.

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

Добавлено
Цитата applegame @
Тут только одно ограничение - рекурсивное инстанцирование.

Источник проблемы вообще в том, что нужно инстанцировать. А корень требования инстанцирования в том, что есть зависимость шаблонного кода от параметров(чего нет в случае ПП).

Автор: applegame 08.07.15, 09:32
Цитата D_KEY @
Я считаю, что нет, т.к. все портит специализация и перегрузка,
А я не считаю, что портит. Ты же не обязан специализировать и перегружать. Не пурегружай и не специализируй и будет тебе ПП. А то у тебя получается, что автомобиль с крыльями - уже не автомобиль, потому что крылья позволяют летать, а автомобиль по определению не летает.
Цитата D_KEY @
Источник проблемы вообще в том, что нужно инстанцировать. А корень требования инстанцирования в том, что есть зависимость шаблонного кода от параметров(чего нет в случае ПП).
Это ничего не меняет. По сути все равно как там разворачиваются шаблоны внутри, пока дело не доходит до рекурсии. И, по моему глубокому убеждению это никак не связано с "истинностью" ПП. Вооще никак. Это просто недостаток C++.

Автор: D_KEY 08.07.15, 09:42
Цитата applegame @
А я не считаю, что портит. Ты же не обязан специализировать и перегружать. Не пурегружай и не специализируй и будет тебе ПП.

Не будет, так как компилятор будет инстанцировать и зависимость никуда не уйдет.

Цитата
А то у тебя получается, что автомобиль с крыльями - уже не автомобиль, потому что крылья позволяют летать, а автомобиль по определению не летает.

Если крылья мешают ездить, то да, это не автомобиль.

Автор: DarkEld3r 08.07.15, 09:44
Цитата D_KEY @
Надо таки посмотреть, что rust скажет по этому поводу.
Ничего не скажет - не получится там это реализовать.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    enum List {
        Cons(u32, Box<List>),
        Nil,
    }

Значения внутри enum считаются именно значениями, а не типами, так что специализацию для них (пока?) сделать не получится. А если делать реализацию для ScalarProduct, то и проверки длины придётся делать руками. Например, как это сделано тут.

Автор: D_KEY 08.07.15, 09:46
Цитата DarkEld3r @
Значения внутри enum считаются именно значениями, а не типами, так что специализацию для них (пока?) сделать не получится.

Но можно же сделать разные типы для Nil и Cons.

Автор: applegame 08.07.15, 09:50
Цитата D_KEY @
Не будет, так как компилятор будет инстанцировать и зависимость никуда не уйдет.
А тебе то какое дело до того что там делает компилятор? Ты смотри на исходники.
Цитата D_KEY @
Если крылья мешают ездить, то да, это не автомобиль.
Тебе мешает специализация и перегрузка?

Автор: D_KEY 08.07.15, 10:34
Цитата applegame @
Цитата D_KEY @
Не будет, так как компилятор будет инстанцировать и зависимость никуда не уйдет.
А тебе то какое дело до того что там делает компилятор?

В том-то и дело, что с ПП мне все равно, что там будет делать компилятор. А в случае с шаблонами - нет. Мне нужно знать, что зависит от параметра шаблона, а что нет, подхватит ли он нужные мне перегрузки и специализации или нет. И т.д.

Цитата
Цитата D_KEY @
Если крылья мешают ездить, то да, это не автомобиль.
Тебе мешает специализация и перегрузка?

Она мне помогает :D Но ПП она мешает, да.

Автор: applegame 08.07.15, 10:37
Цитата D_KEY @
А в случае с шаблонами - нет. Мне нужно знать, что зависит от параметра шаблона, а что нет, подхватит ли он нужные мне перегрузки и специализации или нет. И т.д.
Полагаю это твои личные заморочки. Я начинаю задумываться о глубинах компилятора, только когда что-то оптимизирую или пишу нечто не укладывающееся в ПП. Может этому способствует более приятный синтаксис D, не знаю.

Автор: D_KEY 08.07.15, 11:00
Цитата applegame @
Цитата D_KEY @
А в случае с шаблонами - нет. Мне нужно знать, что зависит от параметра шаблона, а что нет, подхватит ли он нужные мне перегрузки и специализации или нет. И т.д.
Полагаю это твои личные заморочки.

Вообще-то это нужно знать, чтобы решать задачи. Просто ты, как правило, итак это держишь в голове, потому отдельно не обдумываешь. Но тебе нужно знать, что
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template<typename T>
    void foo(T x)
    {
        bar(x);
    }


Вызовет подходящий bar. А в более сложных случаях(методы, неймспейсы и поиск Кенига, перегрузки, специализации и пр. и пр.), это не так просто понять.

Автор: DarkEld3r 08.07.15, 11:18
Цитата D_KEY @
Но можно же сделать разные типы для Nil и Cons.
Как? Вот тут список - это или конец списка или значение и указатель на список:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    enum List {
        Cons(u32, Box<List>),
        Nil,
    }
Если сделать отдельно:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    struct Nil;
    struct Cons(u32, Box<???>);
То что должно быть вместо вопросительных знаков? Да и матчиться по разнородным типам нельзя.

В общем, или я туплю где-то или всё-таки не получится.

Автор: D_KEY 08.07.15, 11:21
Цитата DarkEld3r @
Цитата D_KEY @
Но можно же сделать разные типы для Nil и Cons.
Как? Вот тут список - это или конец списка или значение и указатель на список:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    enum List {
        Cons(u32, Box<List>),
        Nil,
    }
Если сделать отдельно:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    struct Nil;
    struct Cons(u32, Box<???>);
То что должно быть вместо вопросительных знаков? Да и матчиться по разнородным типам нельзя.

В общем, или я туплю где-то или всё-таки не получится.

Там в ЖЖ код на C#, на дженериках.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    using System;
    interface ScalarProduct<A> {
      int scalarProduct(A second);
    }
    class Nil : ScalarProduct<Nil> {
      public Nil(){}
      public int scalarProduct(Nil second) {
        return 0;
      }
    }
    class Cons<A> : ScalarProduct<Cons<A>> where A : ScalarProduct<A> {
      public int value;
      public A tail;
      public Cons(int _value, A _tail) {
        value = _value;
        tail = _tail;
      }
      public int scalarProduct(Cons<A> second){
        return value * second.value + tail.scalarProduct(second.tail);
      }
    }
    class _Test{
      public static int main(int n){
        return _main(n, 0, new Nil(), new Nil());
      }
      public static int _main<A>(int n, int i, A first, A second) where A : ScalarProduct<A> {
        if (n == 0) {
          return first.scalarProduct(second);
        } else {
          return _main(n-1, i+1, new Cons<A>(2*i+1,first), new Cons<A>(i*i, second)); // Works
          //return _main(n-1, i+1, first, new Cons<A>(i*i, second)); // Doesn't work
        }
      }
    }
    public class Test{
      public static void Main(){
        Console.Write("Enter a number: ");
        int val = Convert.ToInt32(Console.ReadLine());
        Console.WriteLine(_Test.main(val));
      }
    }

Автор: applegame 08.07.15, 11:27
Цитата D_KEY @
Но тебе нужно знать, что
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template<typename T>
    void foo(T x)
    {
        bar(x);
    }


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

Добавлено
Тут вот еще Qraizer задавал вопрос, мне тоже интересно. Что значит полиморфный тип? Кто-нибудь может дать внятное определение, на основании которого можно было бы утверждать, что T в шаблоне - это не полиморфный тип.

Автор: D_KEY 08.07.15, 12:03
Цитата applegame @
Для меня это будет полиморфная функция bar.

Возможно. А может ты хотел вызывать метод того же класса, в котором пишешь метод foo, а может еще чего-нибудь.

Цитата
Что значит полиморфный тип? Кто-нибудь может дать внятное определение


Вот, например
https://www.haskell.org/tutorial/goodies.html
Цитата
Haskell also incorporates polymorphic types---types that are universally quantified in some way over all types. Polymorphic type expressions essentially describe families of types. For example, (forall a)[a] is the family of types consisting of, for every type a, the type of lists of a. Lists of integers (e.g. [1,2,3]), lists of characters (['a','b','c']), even lists of lists of integers, etc., are all members of this family. (Note, however, that [2,'b'] is not a valid example, since there is no single type that contains both 2 and 'b'.)


Добавлено
Ну и полиморфная функция аналогично.

Автор: applegame 08.07.15, 12:06
Цитата D_KEY @
Вот, например
И что именно здесь не подходит к шаблонам? Ну кроме названия языка :)

Автор: D_KEY 08.07.15, 12:10
Цитата applegame @
Цитата D_KEY @
Вот, например
И что именно здесь не подходит к шаблонам?

Я уже говорил 100500 раз. Инстанс шаблона зависит от конкретных значений параметров шаблонов. Тип совершенно никак не связан с шаблоном, который его породил. Спор абсолютно бесполезен, это давно видно.

Автор: applegame 08.07.15, 12:12
Цитата D_KEY @
Я уже говорил 100500 раз. Инстанс шаблона зависит от конкретных значений параметров шаблонов. Тип совершенно никак не связан с шаблоном, который его породил. Спор абсолютно бесполезен, это давно видно.
В твоей цитате ничего нет об инстансах, конкретных значениях и т.д. и т.п. Ты мне напиши, что-то вроде: "полиморфный тип - это нечто обладающее такими-то свойствами и вот это свойство отсутствует в шаблонах C++".

Автор: D_KEY 08.07.15, 12:14
Цитата applegame @
В твоей цитате ничего нет об инстансах, конкретных значениях и т.д. и т.п.

Ты спросил, что такое полиморфный тип.

Цитата
Ты мне напиши, что-то вроде: "полиморфный тип - это нечто обладающее такими-то свойствами и вот это свойство отсутствует в шаблонах C++".

Это следует из определения параметрического полиморфизма. В котором четко сказано, что зависимости быть не должно.

Добавлено
Цитата applegame @
В твоей цитате ничего нет об инстансах, конкретных значениях и т.д. и т.п.

Можешь начать с того, что полиморфный тип - это тип, а полиморфная функция - это функция. Шаблон типа/функции в С++ не является типом/функцией.

Автор: applegame 08.07.15, 13:14
Цитата D_KEY @
Это следует из определения параметрического полиморфизма. В котором четко сказано, что зависимости быть не должно.
Зависимости кода от типа? Ну дык в шаблоне без специализации или перегрузки один код для всех типов. Ты говоришь, что компилятор C++ создает в бинарном конечном файле несколько вариантов функций. Ну дык я тебе гарантирую, что компилятор Хаскеля в случае функции суммирования двух переменных тоже создаст несколько разных функций в бинарном файле в зависимости от типа.

Добавлено
Цитата D_KEY @
Шаблон типа/функции в С++ не является типом/функцией.
Правда? Ну тогда скажи мне bar- это функция или шаблон? И является ли тип переменной b - типом?
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int a = bar(b);


Добавлено
Кстати, считаешь ли ты функцию printf - параметрически полиморфной? :)

Автор: DarkEld3r 08.07.15, 13:21
Цитата D_KEY @
Там в ЖЖ код на C#, на дженериках.
Я его видел. :) Но зациклился на определении List через enum.

В любом случае, не получится. Раст тоже будет бесконечно пытаться выводить типы.

Автор: applegame 08.07.15, 13:29
Все, ща D_KEY заявит, что в Rust нет настоящего полиморфизьма! А причина очевидна: В Rust нет виртуальной машины, поэтому он не умеет инстанцировать свои дженерики в run-time как C#. Теперь по D_KEY получается еще, что в Rust и дженериков-то нет, а есть недошаблоны. Буга-гашеньки.

Автор: D_KEY 08.07.15, 13:39
Цитата applegame @
Ну дык в шаблоне без специализации или перегрузки один код для всех типов.

Не будет.

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

Нет, я говорю о том, что он генерирует функции/типы(сущности языка).

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

Это аспект реализации. Если бы он так не делал, ничего бы в языке не поменялось. А в C++ поменялось бы. В этом и разница.

Цитата
Цитата D_KEY @
Шаблон типа/функции в С++ не является типом/функцией.
Правда? Ну тогда скажи мне bar- это функция или шаблон? И является ли тип переменной b - типом?
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int a = bar(b);


Какое это имеет отношение к теме?

Цитата
Кстати, считаешь ли ты функцию printf - параметрически полиморфной? :)

Классическую сишную? Нет.

Автор: MyNameIsIgor 08.07.15, 13:40
Цитата applegame @
Ну дык в шаблоне без специализации или перегрузки один код для всех типов.

Нет, не один. Ещё раз: Стандарт требует генерироваться специализации. Потому если программист не написал пользовательскую специализацию, то компилятор сгенерирует свою. Получается специальный полиморфизм на стероидах.

Автор: D_KEY 08.07.15, 13:42
Цитата applegame @
Все, ща D_KEY заявит, что в Rust нет настоящего полиморфизьма!

В случае сырого языка такие выводы делать тяжело. Вопрос в том, является ли это требованием языка, как в C++. Или это бага в реализации.

Цитата
А причина очевидна: В Rust нет виртуальной машины, поэтому он не умеет инстанцировать свои дженерики в run-time как C#.

run-time и инстанцирование тут ни при чем. В Java его нет(по крайней мере раньше не было). В haskell и ocaml нет виртуальной машины, а полиморфные типы есть.

Добавлено
Цитата DarkEld3r @
Раст тоже будет бесконечно пытаться выводить типы.

Этого требует спецификация языка? Или это особенность реалиазации? Насколько я понимаю, Rust'у ничего не мешает работать более умно. Это в языке ничего не сломает.

Автор: applegame 08.07.15, 13:49
Цитата MyNameIsIgor @
Нет, не один. Ещё раз: Стандарт требует генерироваться специализации. Потому если программист не написал пользовательскую специализацию, то компилятор сгенерирует свою. Получается специальный полиморфизм на стероидах.
Следует ли из этого, что если вдруг в стандарт Хаскеля введут требование генерить специализации (а он по факту во многих случаех их неявно генерит), то Хаскель внезапно перестанет поддерживать ПП?
Цитата D_KEY @
run-time и инстанцирование тут ни при чем. В Java его нет(по крайней мере раньше не было).
Да ну конечно, В жабе автоматическое затирание типов. В плюсах опять же тоже можно сделать затирание, но вручную.
Цитата D_KEY @
В haskell и ocaml нет виртуальной машины, а полиморфные типы есть.
Я точно не знаю как Хаскель и OCaml реализуют рекурсивные специализации, скорее всего затирают тип как жаба.

Автор: MyNameIsIgor 08.07.15, 13:55
Цитата applegame @
Следует ли из этого, что если вдруг в стандарт Хаскеля введут требование генерить специализации (а он по факту во многих случаех их неявно генерит), то Хаскель внезапно перестанет поддерживать ПП?

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

Автор: D_KEY 08.07.15, 13:55
Цитата applegame @
Следует ли из этого, что если вдруг в стандарт Хаскеля введут требование генерить специализации (а он по факту во многих случаех их неявно генерит)

Смотря как генерить :) Можно сломать ПП, а можно не сломать. Ломать свой язык они не станут.

Цитата
В плюсах опять же тоже можно сделать затирание, но вручную.

Потеряв таки типобезопасность. Это не считается :D

Автор: applegame 08.07.15, 13:57
Я в принципе понимаю мысль MyNaneIsIgor. Прописывание в стандарте C++ требований об обязательной специализации ограничивает применимость ПП в C++. Если мы в стандарте C++ разрешим в некоторых случаях не генерить специализации, а выкручиваться другими способами (например затирая тип или даже интерпретируя код шаблона), то это уже будет не современный C++, а например C++20, который по D_KEY наконец-то станет поддерживать ПП.

Автор: D_KEY 08.07.15, 14:00
Цитата applegame @
который по D_KEY наконец-то станет поддерживать ПП

а так же поломает совместимость, потеряет в возможностях и станет нафиг не нужным :D

Автор: MyNameIsIgor 08.07.15, 14:00
Цитата applegame @
Если мы в стандарте C++ разрешим в некоторых случаях не генерить специализации, а выкручиваться другими способами (например затирая тип или даже интерпретируя код шаблона)

Там другие особенности полезут. Например, можно ли привести тип List<A> к List<B>, если A приводимо к B?

Автор: applegame 08.07.15, 14:04
Цитата D_KEY @
а так же поломает совместимость, потеряет в возможностях и станет нафиг не нужным :D
Почему? Допустим, пока ты не используешь бесконечные рекурсивные шаблоны (а сейчас их невозможно использовать совсем) оно работает по-старому. С чего бы это поломается совместимость и потеря возможностей?

Добавлено
Цитата D_KEY @
Потеряв таки типобезопасность. Это не считается :D
Ну в жабе ты это же не засчитал как минус :)

Автор: DarkEld3r 08.07.15, 14:49
Цитата D_KEY @
Этого требует спецификация языка?
Хз, лень искать. В любом случае, она не полная и отражает сложившуюся ситуацию. Ну то есть, если захотят изменить поведение, смотреть будут на то, чтобы код не поломать, а спеку можно и подправить. Реализация-то ведь одна.

Цитата D_KEY @
Насколько я понимаю, Rust'у ничего не мешает работать более умно. Это в языке ничего не сломает.
Так-то да, но в мануале явно говориться, что дженерики создают отдельные функции. Возможно, конечно, что это просто для облегчения понимания разницы между тем как работают дженерики и как простые функции через "трейт-объекты", но честно говоря, сомневаюсь, что реализация (в обозримом будущем) поменяется.

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

С другой стороны, сейчас активно обсуждаются типы высшего порядка, специализация и т.д. Так что может что-то и поменяется.

Автор: applegame 08.07.15, 14:50
Цитата MyNameIsIgor @
Там другие особенности полезут. Например, можно ли привести тип List<A> к List<B>, если A приводимо к B?
Полезут, но кто сказал, что они неразрешимы?

Добавлено
Цитата DarkEld3r @
но честно говоря, сомневаюсь, что реализация (в обозримом будущем) поменяется.
Вряд ли поменяется, потому что без этого можно прекрасно прожить. Многие даже не задумывались об этом, пока не пришел сумасшедший хаскелист и не задал эту странную задачку.

Добавлено
Фактически внутри любого (хоть как-то поддерживающего ПП) статически типизированного языка, реализацию ПП можно условно разделить на два типа: генерация специализаций и различного вида обертки. Обертки могут быть вырожденными (отсутствовать) в некоторых случаях, вроде подсчета длины списка.
Всякие хаскели по всей видимости умеют и так и эдак, так как для них ПП - неотъемлемая часть, без которой язык теряет смысл. В C++ жестко задана только генерация специализаций, в результате бесконечные рекурсии фейлятся.

ИМХО, эти ограничения не дают оснований считать, что в C++ нет ПП. Некоторые считают иначе. Полагаю спор крутится вокруг терминологии, посему предлагаю забить.
Можно сказать что в C++ "утиный" ПП: выглядит как ПП, плавает как ПП и крякает как ПП, значит вероятно, это ПП.

Автор: MyNameIsIgor 08.07.15, 15:11
Цитата applegame @
Полезут, но кто сказал, что они неразрешимы?

Вряд ли разрешимо в C++.

Добавлено
Цитата applegame @
Цитата D_KEY @
Потеряв таки типобезопасность. Это не считается :D
Ну в жабе ты это же не засчитал как минус :)

А можно ткнуть носом где в жабе потерялась типобезовасность?

Добавлено
Цитата applegame @
Можно сказать что в C++ "утиный" ПП: выглядит как ПП, плавает как ПП и крякает как ПП, значит вероятно, это ПП.

Вот именно - пэпэ :crazy:

Автор: applegame 08.07.15, 15:19
Цитата MyNameIsIgor @
А можно ткнуть носом где в жабе потерялась типобезовасность?
Не уверен, что это можно считать потерей типобезопасности, но проблемы таки есть: https://en.wikipedia.org/wiki/Generics_in_J...th_type_erasure

Автор: MyNameIsIgor 08.07.15, 15:43
Цитата applegame @
Не уверен, что это можно считать потерей типобезопасности, но проблемы таки есть

Там разговор идёт об этом нашем примере со списками: в C++ затирание типа приводит к потере возможности сравнения их длины. В Java я такого не вижу.

Автор: applegame 08.07.15, 21:43
Цитата MyNameIsIgor @
Там разговор идёт об этом нашем примере со списками: в C++ затирание типа приводит к потере возможности сравнения их длины.
Нет не ведет. Затирание типов происходит ПОСЛЕ проверки длины, a точнее типов.

В общем, я решил эту задачу на D (со стиранием типов), полагаю решение должно легко портироваться на C++. Никакого говна с бесконечной генерацией типов, оно тут не нужно. Я пошел по пути хаскельного исходника, а не по неверному пути, на который умышленно или по невежеству направил постановщик задачи. Позже распишу подробнее, так как он похоже, сцуко, всех на#$ал :D.
Жду критики:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    import std.stdio;
    import std.conv;
     
    class List {
        int head;
        List tail;
        this(int head_, List tail_) {
            head = head_;
            tail = tail_;
        }
        int scalarProduct(List other) {
            return head * other.head + (tail ? tail.scalarProduct(other.tail) : 0);
        }
    }
     
    class ListN(int N) : List {
        this(int head, List tail) { super(head, tail); }
    }
     
    auto nil() { return cast(ListN!0) null; }
    auto cons(int head, List tail = nil) { return new ListN!1(head, tail); }
    auto cons(int N)(int head, ListN!N tail) { return new ListN!(N + 1)(head, tail); }
     
    void main() {
        
        int n;
        readf("%s", &n);
        
        int main_(int M, int N)(int n, int i, ListN!N as, ListN!M bs) if(M == N) {
            return ((int n, int i, List as, List bs) {
                if(n == 0) {
                    return as.scalarProduct(bs);
                } else {
                    return main_(n - 1, i + 1, cons(2*i+1, as), cons(i*i, bs));
                    //return main_(n - 1, i + 1, cons(2*i+1, cons(1, as)), cons(i*i, cons(2, bs))); // compiles
                    //return main_(n - 1, i + 1, cons(2*i+1, as), nil);                             // fails
                    //return main_(n - 1, i + 1, cons(3, cons(2*i+1, as)), cons(i*i, bs));          // fails
                }
            })(n, i, as, bs);
        }
     
     
        int calc()(int n) {
            return main_(n, 0, nil, nil);
            //return main_(n, 0, cons(2), cons(3)); // compiles
            //return main_(n, 0, nil, cons(1));     // fails
        }
        
        writeln(calc(n));
    }


Автор: applegame 08.07.15, 22:10
Вот это, для наглядности:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int main_(int M, int N)(int n, int i, ListN!N as, ListN!M bs) if(M == N)
Можно легко заменить на:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int main_(T)(int n, int i, T as, T bs)

Автор: MyNameIsIgor 08.07.15, 22:16
Цитата applegame @
Жду критики

Легко.
Цитата applegame @
Позже распишу подробнее, так как он похоже, сцуко, всех на#$ал

Неа. Он, конечно, сцуко, но прав.

Автор: Qraizer 08.07.15, 22:20
Цитата D_KEY @
Я считаю, что нет, т.к. все портит специализация и перегрузка, которая делает код обобщенной функции/класса зависимым от типов-параметров, что прямо противоречит определению ПП
Не используй специализацию и/или перегрузку. Поверь на слово, это возможно. Скажу даже больше: оказывается, вполне реально не использовать квалификацию у виртуальных методов. Да-да, ей богу не вру.
D_KEY, может хватит валенком прикидываться, а?

Добавлено
Цитата D_KEY @
В том-то и дело, что с ПП мне все равно, что там будет делать компилятор. А в случае с шаблонами - нет. Мне нужно знать, что зависит от параметра шаблона, а что нет, подхватит ли он нужные мне перегрузки и специализации или нет. И т.д.
Давай я открою ещё одну страшную тайну. Когда ты используешь шаблон, тебе не надо ничего этого знать. Оно само работает. Если вдруг понадобилось, значит с вероятностью 86% у тебя хреновый код или с вероятностью 14% хреновый код у автора шаблона.

Автор: applegame 08.07.15, 22:38
Цитата MyNameIsIgor @
Легко.
Ну ты исправил само затирание, не думаю, что это по правилам.

Автор: MyNameIsIgor 08.07.15, 22:41
Цитата applegame @
Цитата MyNameIsIgor @
Легко.
Ну ты исправил само затирание, не думаю, что это по правилам.

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

Добавлено
Иными словами, если инкапсулируете что-то, то это облегчит тестирование, но гарантию не даст, ибо это тоже код.

Автор: applegame 08.07.15, 22:51
Цитата MyNameIsIgor @
Конечно, я исправил само затирание. Я об этом и говорил выше, что при ручном затирании будет место для ошибки. И да, это по правилам - вот пишу я эту функцию main_, приходится мне ручками затирать тип, не уследил и ошибся.
Ну я не знаю. Взять и исправить, грубо говоря
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    List ea = a;

на
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    List ea = пишу что захочу;

Я могу с тем же успехом заменить в Хаскельном исходнике n - 1 на n + 1 и оно уйдет в переполнение стека.

Автор: Qraizer 08.07.15, 22:52
Цитата MyNameIsIgor @
Нет, не один. Ещё раз: Стандарт требует генерироваться специализации. Потому если программист не написал пользовательскую специализацию, то компилятор сгенерирует свою.
:lool: Я наконец-то понял, в чём косяк,applegame! Эти кадры называют специализацией любую конкретизацию! Ононокак. Ты их не переубедишь. Любая машина, что с крыльями, что без, не машина, потому что внутри у каждого из экземпляров детали все с разными серийниками, и у каждой якобы "машины" уникальный номерной знак. Не важно совершенно, что все они одной модели и серии, важно то, что любой сошедший с конвейера экземпляр отличается от чертежей конструктора, а ведь именно там, на чертеже, надписано "машина". Ты понял, да? В общем, кому ехать, а кому-то шашечки.

Автор: applegame 08.07.15, 22:54
Цитата Qraizer @
:lool: Я наконец-то понял, в чём косяк,applegame! Эти кадры называют специализацией любую конкретизацию! Ононокак. Ты их не переубедишь. Любая машина, что с крыльями, что без, не машина, потому что внутри у каждый из экземпляров детали все с разными серийниками, и у каждой уникальный номерной знак. Не важно совершенно, что все они одной модели и серии, важно то, что любой сошедший с конвейера экземпляр отличается от чертежей конструктора, а ведь именно там, на чертеже, надписано "машина". Ты понял, да? В общем, кому ехать, а кому-то шашечки.
Да, я уже понял это. Именно так и обстоят дела.

Автор: MyNameIsIgor 08.07.15, 22:54
Цитата applegame @
Я могу с тем же успехом заменить в Хаскельном исходнике n - 1 на n + 1 и оно уйдет в переполнение стека. Нет?

Да. Из этого будет однозначный вывод: хаскель не может детектировать переполнение стека при компиляции (или может, но программист это не сделал - но это другая история). Но поставленную задачу - статически гарантировать обработку списком одинакового размера - он выполнил. А в C++, D и т.п. мы не гарантировали ничего: ни списков, ни стека :)

Автор: applegame 08.07.15, 23:01
Цитата MyNameIsIgor @
Да. Из этого будет однозначный вывод: хаскель не может детектировать переполнение стека при компиляции (или может, но программист это не сделал - но это другая история). Но поставленную задачу - статически гарантировать обработку списком одинакового размера - он выполнил. А в C++, D и т.п. мы не гарантировали ничего: ни списков, ни стека :)
Я не согласен и покажу это завтра. Вы зафейлили генерацию списков, но ее можно переписать так, что уже не зафейлишь. Ибо само заполнение к задаче имеет отношение, чуть менее, чем никакое.

Автор: MyNameIsIgor 08.07.15, 23:07
Цитата applegame @
Ибо само заполнение к задаче имеет отношение, чуть менее, чем никакое.

С чего бы? :D А в чём, по-вашему, задача? Задача в том, чтобы гарантировать обработку списков одинаковой длины. А ошибиться программист может в любом месте кода.

Автор: Qraizer 08.07.15, 23:10
Цитата applegame @
но ее можно переписать так, что уже не зафейлишь.
applegame, задача простая даже для C++03. Подсказать?

Автор: MyNameIsIgor 08.07.15, 23:20
Цитата Qraizer @
Я наконец-то понял, в чём косяк,applegame! Эти кадры называют специализацией любую конкретизацию! Ононокак. Ты их не переубедишь.

Я даже не буду спрашивать из какого носа выковыряли термин "конкретизация", а просто скажу: кадры из Комитета тоже считают, что инстанцирование шаблона приводит к его неявной специализации, если он не был специализирован явно.
Так и пишут, сволочи такие
Цитата ISO/IEC 14882, 14.7/4
An instantiated template specialization can be either implicitly instantiated (14.7.1) for a given argument
list or be explicitly instantiated (14.7.2). A specialization is a class, function, or class member that is either
instantiated or explicitly specialized (14.7.3).

Цитата ISO/IEC 14882, 14.7.1/1
Unless a class template specialization has been explicitly instantiated (14.7.2) or explicitly specialized (14.7.3),
the class template specialization is implicitly instantiated when the specialization is referenced in a context
that requires a completely-defined object type or when the completeness of the class type affects the semantics
of the program.

Цитата ISO/IEC 14882, 14.7.1/3
Unless a function template specialization has been explicitly instantiated or explicitly specialized, the function
template specialization is implicitly instantiated when the specialization is referenced in a context that
requires a function definition to exist.

Ну, и далее по тексту.

А теперь обращение к нормальным людям: да пусть даже инстанцирование не приводит к появлению специализаций, но чем сгенерированные при инстанцировании типы/функции отличаются от явных специализаций, который есть ad-hoc полиморфизм?
Цитата Qraizer @
В общем, кому ехать, а кому-то шашечки.

Мы тут пытаемся ехать. Кто-то нам даже обещал, что ехать получится на "азах метакодирования", но всё закончилось балабольством.

Автор: Qraizer 09.07.15, 00:19
Ты не открыл Америку, MyNameIsIgor. Некто, то ли Вандевурд, то ли Джосаттис, запамятовал, кто из них из EDG, которая единственная в мире реализовала экспорт шаблонов, а может и оба, английским по белому возмущались непродуманностью терминологии в Стандарте, что может запутать, если не читать его очень вдумчиво. Вот прям как ты возмущались. Моя терминология, кстати, от них. Можешь их тоже причислить к ненормальным. Впрочем, есть другой вариант: почитать Стандарт вдумчиво. Просто намекну.
Цитата MyNameIsIgor @
но чем сгенерированные при инстанцировании типы/функции отличаются от явных специализаций, который есть ad-hoc полиморфизм?
Ответ есть в Стандарте. Они качественные, с кучей следствий из этого.
А своё эго можешь потешить, сказав какое-нибудь слово последним. Я не буду ничего доказывать, ни одним из четырёх способов, три из которых доступны ещё с C++03, если не ещё раньше. Ибо возвращаю:
Цитата MyNameIsIgor @
Возможно есть, возможно - нет. В любом случае я хочу, чтобы слушающий обладал достаточным интеллектом, чтобы понять сказанное.
За сим откланиваюсь.

Добавлено
P.S. Четыре – не предел.

Автор: MyNameIsIgor 09.07.15, 00:48
Цитата Qraizer @
Четыре – не предел.

Вы ещё поглубже затянитесь - там и пятый с шестым подоспеют.

Автор: D_KEY 09.07.15, 07:11
Qraizer, не очень понимаю, в чем смысл твоих последних постов? "Я все знаю, я все умею, а вы кадры, я вам ничего не скажу, я вам ничего не покажу, хотя знаю 42 способа". Мог бы вообще ничего не писать :-? Иногда лучше жевать.

Добавлено
Цитата Qraizer @
Не используй специализацию и/или перегрузку. Поверь на слово, это возможно. Скажу даже больше: оказывается, вполне реально не использовать квалификацию у виртуальных методов. Да-да, ей богу не вру.

И это как-то заставит компилятор не генерировать типы и функции?

Цитата
D_KEY, может хватит валенком прикидываться, а?

Может хватит строить из себя неизвестно кого и переходить на личности? Не пора ли начать аргументировать свою позицию? Например, цитатами из стандарта.

Цитата
Давай я открою ещё одну страшную тайну. Когда ты используешь шаблон, тебе не надо ничего этого знать. Оно само работает. Если вдруг понадобилось, значит с вероятностью 86% у тебя хреновый код или с вероятностью 14% хреновый код у автора шаблона.

Мы говорили о написании шаблона, а не использовании его.

Автор: Qraizer 09.07.15, 08:12
Почему я должен был бы это делать, а вы, D_KEY, нет? Вам дали определение. Вы его проигнорировали, упёршись в реализацию. Это был аргумент, что ли? Ну так динамический полиморфизм тоже реализован посредством таблицы указателей, которая к слову может быть реализована на C, и именно так и реализуется, если кому надо, значит C++ не поддерживает динамический полиморфизм? Как это опровергать, кроме как отослать к учебникам? Ты красиво воспользовались возможностью Плюсов выключить полиморфизм, в результате чего подытожил, что эта возможность означает отсутствие поддержки полиморфизма. Я честно попытался указать тебе ошибочность такой точки зрения. И что? В качестве методички по основам затронутого материала можно ознакомиться с популярным изложением матчасти, которая лежала всё это время перед вашими глазами. Потом можно будет перейти к серьёзной литературе. Можете ваше виденье критериев соответствия оставить при себе для Никонова с Мастеровым.
Что касается личностей, так это его слова были. Если он так крут, чтобы наезжать на DarkEld3r, пусть сначала сам за себя ответит. И да, я не буду сюда постить цитаты из Стандарта. Отличия размазаны по всей главе, посвящённой шаблонам, а она большая.

P.S. Один из 4-ёх вариантов могу обрисовать прям счас без всякого кода. Он настолько элементарен, что о нём легко забыть. А именно: ... ничего для контроля длины не писать. Внезапно, да? При разной длине списков код гарантировано не скомпилится. Требование выполнено.

Добавлено
P.P.S. В последний раз. Шаблоны полностью отвечают затронутому вопросу. Метакод прекрасно описывается алгеброй типизированного лямбда-исчисления. Ограничения шаблонов на возможность работы только с константными сущностями, будь то значения или типы, накладывают определённые ограничения на сферу их применимости. С этим спорить некто не собирался. Метакод не может менять сущности, но может создавать новые на основе существующих. Значения-типы в частности. Так что спорить с тем, что шаблоны реализуют параметрический полиморфизм, это то же, что утверждать, мол, фронт-энд компилятор с C++ в C – это не компилятор C++. Тогда и gcc не компилятор. И clang тоже. Удачи с бобами.

Автор: D_KEY 09.07.15, 08:52
Цитата Qraizer @
Почему я должен был бы это делать, а вы, D_KEY, нет? Вам дали определение.

Из определения ПП следует отсутствие зависимости кода(исходного, а не бинарного) от параметров. В C++ эта зависимость существует(в haskell, java, c# - нет). Отсюда и требование обязательной специализации, которое и мешает решить задачу. Дженерики и полиморфные типы обеспечивают типобезопасность без специализации всего и вся(т.к. они не обеспечивают специальный полиморфизм, который обеспечивают шаблоны).

Добавлено
Цитата Qraizer @
Вы его проигнорировали, упёршись в реализацию.

В том и дело, что для C++ это вопрос не реализации, а спецификации языка. Вот в случае полиморфных типов или дженериков(хотя Rust, да) это вопрос реализации, будет она в отдельных случаях что-то специализировать или нет.

Добавлено
Цитата Qraizer @
Ну так динамический полиморфизм тоже реализован посредством таблицы указателей, которая к слову может быть реализована на C, и именно так и реализуется, если кому надо, значит C++ не поддерживает динамический полиморфизм?

Нет, не означает.

Цитата
Ты красиво воспользовались возможностью Плюсов выключить полиморфизм, в результате чего подытожил, что эта возможность означает отсутствие поддержки полиморфизма.

Я тебе уже говорил. Полиморфизм есть. Но это не параметрический полиморфизм, а специальный.

Цитата
Я честно попытался указать тебе ошибочность такой точки зрения. И что? В качестве методички по основам затронутого материала можно ознакомиться с популярным изложением матчасти, которая лежала всё это время перед вашими глазами. Потом можно будет перейти к серьёзной литературе.

Могу дать тот же совет тебе :-?

Цитата
Метакод прекрасно описывается алгеброй типизированного лямбда-исчисления.

Допустим.

Цитата
Ограничения шаблонов на возможность работы только с константными сущностями, будь то значения или типы, накладывают определённые ограничения на сферу их применимости.

Да.

Цитата
Метакод не может менять сущности, но может создавать новые на основе существующих.

Да.

Цитата
Значения-типы в частности.

Ок.


Цитата
Так что спорить с тем, что шаблоны реализуют параметрический полиморфизм

Не понимаю, как из предыдущих утверждений следует наличие параметрического полиморфизма.

Автор: applegame 09.07.15, 09:04
Вторая часть марлезонского балета. Из main_ удалена лямбда и теперь она выглядит практически как в хаскельном варианте. Эта дыра закрыта для ваших шаловливых ручонок. Ищите другие дыры:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    import std.stdio;
     
    class Base {
        int head;
        Base tail;
        this(int head, Base tail) { this.head = head; this.tail = tail; }
    }
     
    class List(int N) : Base {
        this(ARGS...)(ARGS args) { super(args); }
    }
     
    class ListN(int N) : List!N {
        this(ARGS...)(ARGS args) { super(args); }
    }
     
    int scalarProduct(T)(T as, T bs) {
        if(!as) return 0;
        return as.head * bs.head + scalarProduct(as.tail, bs.tail);
    }
     
    auto cons(int N)(int head, List!N tail) { return new ListN!1(head, tail); }
    auto cons(int N)(int head, ListN!N tail) { return new ListN!(N + 1)(head, tail); }
     
    List!0 nil;
    auto cons(int head) { return cons(head, nil); }
     
    int main_(int N)(int n, int i, List!N as, List!N bs) {
        if(n == 0) {
            return scalarProduct(as, bs);
        } else {
            return main_(n - 1, i + 1, cons(2*i + 1, as), cons(i*i, bs));
            //return main_(n - 1, i + 1, cons(2*i+1, cons(1, as)), cons(i*i, cons(2, bs))); // compiles
            //return main_(n - 1, i + 1, cons(2*i+1, as), nil);                             // fails
            //return main_(n - 1, i + 1, cons(3, cons(2*i+1, as)), cons(i*i, bs));          // fails
        }
    }
     
    int main_(int n) {
        return main_(n, 0, nil, nil);
        //return main_(n, 0, cons(2), cons(3)); // compiles
        //return main_(n, 0, nil, cons(1));     // fails
    }
     
    void main() {
        int n;
        readf("%s", &n);
        main_(n).writeln;
    }

Автор: D_KEY 09.07.15, 09:21
Qraizer, applegame

Тут есть параметрический полиморфизм?

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    class A {
    public:
       virtual void foo()
       {
            // default implementation
       }
    };
     
    void bar(A & a)
    {
        a.foo();
    }


А если нет, то можете объяснить принципиальную разницу с шаблонами(в контексте нашего разговора, естественно)? Ведь ваш аргумент на счет "не специализируй" работает и тут :-?

Автор: applegame 09.07.15, 09:24
Цитата D_KEY @
Из определения ПП следует отсутствие зависимости кода(исходного, а не бинарного) от параметров. В C++ эта зависимость существует
В исходном коде??? :facepalm:
Цитата D_KEY @
В том и дело, что для C++ это вопрос не реализации, а спецификации языка.
Реализация и спецификация не являются взаимоисключающими понятиями. Да, в спецификации C++ жестко указан способ реализации параметрического полиморфизма.

Добавлено
Цитата Qraizer @
applegame, задача простая даже для C++03. Подсказать?
Решение возможно есть, но я не уверен, что это можно реализовать так, чтобы MyNameIsIgor не смог испортить :). А он, похоже, мастер в этом деле.

Автор: D_KEY 09.07.15, 09:29
Цитата applegame @
Цитата D_KEY @
Из определения ПП следует отсутствие зависимости кода(исходного, а не бинарного) от параметров. В C++ эта зависимость существует
В исходном коде??? :facepalm:

Возможно неверено выразился. Имел в виду, что C++ генерирует сущности языка. И речь именно о них, а не о машинном коде.

Цитата
Цитата D_KEY @
В том и дело, что для C++ это вопрос не реализации, а спецификации языка.
Реализация и спецификация не являются взаимоисключающими понятиями. Да, в спецификации C++ жестко указан способ реализации параметрического полиморфизма.

Нет. Там описаны шаблоны, а не реализация параметрического полиморфизма.

Автор: MyNameIsIgor 09.07.15, 09:33
Цитата Qraizer @
В качестве методички по основам затронутого материала можно ознакомиться с популярным изложением матчасти, которая лежала всё это время перед вашими глазами.

:lool: С системой F мы как раз таки в состоянии сделать проверку типов, если их получается бесконечное количество как инстансов полиморфных типов. А C++, D, Rust - не в состоянии.

Добавлено
Цитата applegame @
Эта дыра закрыта для ваших шаловливых ручонок. Ищите другие дыры

Она всё там же.
Мы так и будем играть, или вы таки поймёте, что если затирать типы до их проверки, то на эту проверку уже можно не полагаться?

Автор: applegame 09.07.15, 09:50
Цитата MyNameIsIgor @
Она всё там же.
Мы так и будем играть, или вы таки поймёте, что если затирать типы до их проверки, то на эту проверку уже можно не полагаться?
Ну это уже совсем криминал. Может вынести всю фигню вам в отдельный модуль и заприватить конструкторы? ИМХО, это уже нарушение условий задачи. Думаете в жабовом и шарповом варианте не удасться аналогично похерить эти проверки?

Автор: D_KEY 09.07.15, 09:53
Цитата applegame @
это уже нарушение условий задачи.

В чем?

Цитата
Думаете в жабовом и шарповом варианте не удасться аналогично похерить эти проверки?

Аналогично? Покажи.

Автор: applegame 09.07.15, 09:59
Цитата D_KEY @
В чем?
В том, что делается попытка использовать "скрытые" типы, которые совсем скрыть от юзверя невозможно в силу гибкости языка.
Даже если я запривачу все конструкторы наглухо, всегда можно влезть в сами cons'ы. А это совсем не то, что обсуждается в задаче.

Автор: D_KEY 09.07.15, 10:20
Цитата applegame @
Цитата D_KEY @
В чем?
В том, что делается попытка использовать "скрытые" типы, которые совсем скрыть от юзверя невозможно в силу гибкости языка.
Даже если я запривачу все конструкторы наглухо, всегда можно влезть в сами cons'ы. А это совсем не то, что обсуждается в задаче.

Эм. Да ты хоть обзакрывайся :)
Кто помешает написать в main_ так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    List!1 as2 = cons(i, as);
    return main_(n - 1, i + 1, cons(2*i + 1, as2), cons(i*i, bs));

?

Автор: applegame 09.07.15, 10:31
Цитата D_KEY @
Эм. Да ты хоть обзакрывайся :)
Ага, то есть если тебе не удасться испортить код не влезая в cons'ы то ты признаешь решение задачи? Я ведь могу сами типы List!N и ListN!N скрыть, даже внутри main_.

Добавлено
Цитата applegame @
Я ведь могу сами типы List!N и ListN!N скрыть, даже внутри main_.
Хотя это скорее всего не поможет. Но ты фактически искусственно испортил тип выражения cons(i, as), ты бы еще туда cast() вписал. Правильнее было бы
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    auto as2 = cons(i, as);

Иожет таки не будем упоминать List!N и ListN!N всуе? Для конструирования списка как и в хаскеле предусмотрена своя сущность - cons. Нахрена вручную портить тип списка?
А то как-то нехорошо получается. В Хаскеле даже переменную создать невозможно, а в D вы их создаете как вам вздумается. В Хаскеле типы выводятся из выражений, а в D вы почему-то решили, что указывать типы вручную - по правилам. Не, друзья, так дело не пойдет.

Автор: OpenGL 09.07.15, 10:41
Цитата applegame @
ИМХО, это уже нарушение условий задачи.

О какой задаче вы уже третий день спорите? :)

Автор: applegame 09.07.15, 10:44
Цитата OpenGL @
О какой задаче вы уже третий день спорите? :)
О вот этой - https://www.linux.org.ru/forum/development/4300872

Автор: OpenGL 09.07.15, 10:50
Не понял задачу. Как во время компиляции можно определить длину списка, которая известна только в рантайме? Имхо, задача поставлена некорректно.

Автор: applegame 09.07.15, 10:56
Цитата OpenGL @
Не понял задачу. Как во время компиляции можно определить длину списка, которая известна только в рантайме? Имхо, задача поставлена некорректно.
Нормально поставлена, хоть и не без отвлекающих маневров. Мне понадобился целый день, чтобы понять что же хочет нам доказать афтырь. В смысле не то что я думал весь день. Просто я читал этот код, в течение дня периодически обдумывал и только к вечеру до меня дошло.

Автор: MyNameIsIgor 09.07.15, 10:58
Цитата applegame @
Ну это уже совсем криминал. Может вынести всю фигню вам в отдельный модуль и заприватить конструкторы?

И каким образом вы мне гарантируете, что в этом вашем модуле нет ошибок с обработкой списков разной длины, например?
Цитата applegame @
а в D вы их создаете как вам вздумается

Не принимается. Мы создаём её с ошибкой, потому что предоставленная вами иерархия позволяет создать с ошибкой. В том же решении на шарпе такого не было.
Цитата applegame @
Ага, то есть если тебе не удасться испортить код не влезая в cons'ы

Ни на чём не основанное условие.
Цитата applegame @
то ты признаешь решение задачи?

Вы хоть перед самим собой будьте честным.

Автор: D_KEY 09.07.15, 10:59
Цитата OpenGL @
Как во время компиляции можно определить длину списка, которая известна только в рантайме?

Никак. Ее и не надо определять.

Автор: MyNameIsIgor 09.07.15, 11:00
Цитата OpenGL @
Не понял задачу. Как во время компиляции можно определить длину списка, которая известна только в рантайме?

Её не надо определять во время компиляции. Надо просто доказать, что мы обрабатываем два списка одинаковой длины.
Цитата OpenGL @
Имхо, задача поставлена некорректно.

Задача абсолютно корректна.

Автор: D_KEY 09.07.15, 11:02
Цитата applegame @
Цитата applegame @
Я ведь могу сами типы List!N и ListN!N скрыть, даже внутри main_.
Хотя это скорее всего не поможет.

Ага.

Цитата
Но ты фактически искусственно испортил тип выражения cons(i, as), ты бы еще туда cast() вписал.

Я сделал все типобезопасно с точки зрения языка. Наследника можно воспринимать как предка. is-a и все такое.
Какие ко мне вопросы?

Цитата
Иожет таки не будем упоминать List!N и ListN!N всуе?

И как тогда написать main_?

Цитата
А то как-то нехорошо получается. В Хаскеле даже переменную создать невозможно, а в D вы их создаете как вам вздумается.

Хм. Что такого я сделал? Ты сам это делаешь при вызове main_ :-?

Автор: OpenGL 09.07.15, 11:10
А если я сгенерирую списки с рандомной длиной? Как компилятор это вообще сможет проверить?

Автор: D_KEY 09.07.15, 11:10
Цитата OpenGL @
А если я сгенерирую списки с рандомной длиной? Как компилятор это вообще сможет проверить?

Попробуй еще тут посмотреть


В ЖЖ есть код на C#.

Автор: OpenGL 09.07.15, 11:12
А, ну вот цитату нашёл.
Цитата
В данном случае - совпадение длин двух массивов. Чтобы программа, допускающая, что массивы могут оказаться разной длины, не скомпилировалась. Я показал, как это делается, а мой оппонент www_linux_org_ru показал строчку, нарушающую этот инвариант, и убедился, что она не компилируется.

Т.е. в моём примере выше может быть так, что массивы будут разной длины - ошибка компиляции. Интересно.

Автор: D_KEY 09.07.15, 11:13
Цитата OpenGL @
А если я сгенерирую списки с рандомной длиной? Как компилятор это вообще сможет проверить?

Он проверяет корректность с точки зрения типизации и параметрического полиморфизма. А сами типы составлены так, что если ты попытаешься сформировать списки разной длины, то компилятор тебе об этом скажет. На C++/D/Rust(хотя вот в Rust могли бы решить иначе) это приводит к бесконечному инстанцированию(что решается стиранием типа вручную, которое же приводит к тому, что задача решена не полноценно и проверки легко обходятся в коде без каких-либо хаков).

Добавлено
Цитата OpenGL @
Т.е. в моём примере выше может быть так, что массивы будут разной длины - ошибка компиляции. Интересно.

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

Автор: applegame 09.07.15, 11:19
Цитата MyNameIsIgor @
И каким образом вы мне гарантируете, что в этом вашем модуле нет ошибок с обработкой списков разной длины, например?
А каким образом вы мне гарантируете что в этом вашем компиляторе Хаскеля нет ошибок? Так или иначе мы должны постулировать истинность некоторых частей кода. В D нет встроенной функциональности для работы с такого рода списками, ее приходится эмулировать и постулировать верность этой эмуляции, иначе задача теряет смысл.
Цитата MyNameIsIgor @
Не принимается. Мы создаём её с ошибкой, потому что предоставленная вами иерархия позволяет создать с ошибкой.
Я не предоставляю вам такой иерархии, и она вам не нужна для генерации списков. Но я также не могу ее скрыть, поэтому вы имеете к ней доступ. В Хаскеле она скрыта изначально - все типы выводятся автоматически. Языки ставятся в неравные условия.
Цитата MyNameIsIgor @
В том же решении на шарпе такого не было.
С этим согласен, но надо еще посмотреть код. Вы сами-то пытались его сломать?

Автор: MyNameIsIgor 09.07.15, 11:23
Цитата applegame @
А каким образом вы мне гарантируете что в этом вашем компиляторе Хаскеля нет ошибок? Так или иначе мы должны постулировать истинность некоторых частей кода.

Вот мы и постулировали - компилятор правильный. Некоторым этого достаточно :)
Цитата applegame @
Я не предоставляю вам такой иерархии, и она вам не нужна для генерации списков. Но я также не могу ее скрыть, поэтому вы имеете к ней доступ

Ну, как уже спрашивал D_KEY, как работать то с ними, если они скрыты? Вам придётся их открыть, чтобы компилятор на их основе хоть что-то доказывал.
Цитата applegame @
С этим согласен, но надо еще посмотреть код. Вы сами-то пытались его сломать?

Сломать то можно - вставить обращение по null или деление ноль. Только к спискам отношения не будет иметь.

Добавлено
Цитата applegame @
В Хаскеле она скрыта изначально - все типы выводятся автоматически. Языки ставятся в неравные условия.

Каким образом вывод типов их скрывает? Они отнюдь не скрыты.

Автор: applegame 09.07.15, 11:26
Цитата D_KEY @
Хм. Что такого я сделал? Ты сам это делаешь при вызове main_ :-?
Нет не делаю. Полагаю ты видишь разницу между List!N и List!1? В main_ тип выводится автоматически, а ты указываешь его явно.

Добавлено
Цитата MyNameIsIgor @
Каким образом вывод типов их скрывает? Они отнюдь не скрыты.
Может я слишком плохо знаю Хаскель. Возьмем два выражения для этой задачи:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Сons i*i as

и
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Cons i*i (Cons i*i as)

Они, имеют один тип, но компилятор может эти типы как-то различить. То есть где-то там внутри есть некое дополнительное свойство - скрытый "тип". Есть ли у меня возможность принудительно скастовать второе выражение к первому, так чтобы компилятор продолжал считать эти типы разными?

В D мне удалось сделать так, чтобы выражения cons(i*i, as) и cons(i*i, cons(i*i, as) имели автоматически выводимф разный тип. А вы своим вмешательством его меняете.

Автор: MyNameIsIgor 09.07.15, 11:35
Цитата applegame @
Они, имеют один тип, но компилятор может эти типы как-то различить. То есть где-то там внутри есть некое дополнительное свойство - скрытый "тип".

Нет, они имеют разные типы.
Цитата applegame @
Есть ли у меня возможность принудительно скастовать второе выражение к первому, так чтобы компилятор продолжал считать эти типы разными?

Нет, просто потому, что это преобразование незаконно.

Автор: applegame 09.07.15, 11:36
Цитата MyNameIsIgor @
Нет, они имеют разные типы.
А. ну тогда тем более.
Цитата MyNameIsIgor @
Нет, просто потому, что это преобразование незаконно.
Да. А в D - законно. Может не будем тогда незаконно менять тип возвращаемый cons и незаконно конструировать списки неверного типа?

Добавлено
Цитата MyNameIsIgor @
Вот мы и постулировали - компилятор правильный. Некоторым этого достаточно :)
Почему бы нам тогда не постулировать, что эмуляция генератора списка - верная. Ведь если ее нет в компиляторе мы вынуждены ее эмулировать.

Автор: MyNameIsIgor 09.07.15, 11:41
Цитата applegame @
Да. А в D - законно. Может не будем тогда незаконно менять тип возвращаемый cons и незаконно конструировать списки неверного типа?

Ничего не понял. Если в D она законна, то почему незаконно сконструировали?
Цитата applegame @
Почему бы нам тогда не постулировать, что эмуляция генератора списка - верная. Ведь если ее нет в компиляторе мы вынуждены ее эмулировать.

А почему просто не постулировать, что вы задачу решили, опозорили всех сомневающихся и покинули поле брани на белом коне?
Ещё раз: попытайтесь быть честным хотя бы перед самим собой.

Автор: applegame 09.07.15, 11:46
Цитата MyNameIsIgor @
Ничего не понял. Если в D она законна, то почему незаконно сконструировали?
Потому что в Хаскеле незаконно.
Цитата MyNameIsIgor @
Ещё раз: попытайтесь быть честным хотя бы перед самим собой.
Перед собой я честен. Вам пока не удалось меня убедить, что задача не решена. Лучше конечно спросить у автора. Потому что я полагаю, что вы добавляете туда какие-то новые правила. Следующая моя цель сломать джавовский вариант через подмену типов. Если не удасться - то я признаю, что был не прав.

Автор: MyNameIsIgor 09.07.15, 11:49
Цитата applegame @
Потому что в Хаскеле незаконно.

:wacko:
Цитата applegame @
Перед собой я честен.

Ууууууу... Ну, тогда беда-пичаль.

Автор: applegame 09.07.15, 11:55
Кто может вот этот код переделать так, чтобы он компилялся? А то я не догоняю, как это заставить работать в ideone - http://ideone.com/1S3L6V
Цитата MyNameIsIgor @
Ууууууу... Ну, тогда беда-пичаль.
Может и беда, а может и нет. Посмотрим.

Автор: MyNameIsIgor 09.07.15, 11:57
Цитата applegame @
Кто может вот этот код переделать так, чтобы он компилялся? А то я не догоняю, как это заставить работать в ideone - http://ideone.com/1S3L6V

Компилятор же говорит, что надо сделать - http://ideone.com/iglyDq

Автор: D_KEY 09.07.15, 12:28
Цитата applegame @
Цитата D_KEY @
Хм. Что такого я сделал? Ты сам это делаешь при вызове main_ :-?
Нет не делаю. Полагаю ты видишь разницу между List!N и List!1? В main_ тип выводится автоматически, а ты указываешь его явно.

Эм. Возможно я чего-то и не понял, но разве вот тут:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    main_(n - 1, i + 1, cons(2*i + 1, as), cons(i*i, bs));

При вызове cons вызывается не первая перегрузка?
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    auto cons(int N)(int head, List!N tail) { return new ListN!1(head, tail); }

А разве при передачи в main_ ListN!1 не кастуется к List!1?

Автор: DarkEld3r 09.07.15, 12:31
Цитата D_KEY @
хотя вот в Rust могли бы решить иначе
Я попробовал сделать без генерации типов, на "трейт-обьектах" (интерфейсах), но снова не получилось, уже изз-а других особенностей языка.

Но вообще как это (в варианте с дженериками) сделать без потери эффективности?

Автор: D_KEY 09.07.15, 12:31
Цитата applegame @
Цитата MyNameIsIgor @
Нет, просто потому, что это преобразование незаконно.
Да. А в D - законно. Может не будем тогда незаконно менять тип возвращаемый cons и незаконно конструировать списки неверного типа?

Если в D это операция законна, то значит все правильно написано, а значит мы все меняем законно. Ведь речь как раз идет о возможностях системы типов.

Добавлено
Цитата DarkEld3r @
Но вообще как это (в варианте с дженериками) сделать без потери эффективности?

Сначала затайпчекать(тут не потребуется инстанцирования, т.к. в Rust нет специализаций и перегрузок), а потом генерировать специализации исключительно с целью оптимизаций.

Добавлено
Это если вопрос о реализации в языке :)

Автор: MyNameIsIgor 09.07.15, 12:37
Цитата D_KEY @
генерировать специализации исключительно с целью оптимизаций

Когда остановить то эту генерацию? Или во время исполнения?

Автор: applegame 09.07.15, 12:39
Цитата D_KEY @
При вызове cons вызывается не первая перегрузка?
Она есть, но делается неявно компилятором.
Цитата D_KEY @
А разве при передачи в main_ ListN!1 не кастуется к List!1?
Это частный случай. Точнее будет сказать: ListN!N кастуется к List!N - это полиморфные типы. Ну или псевдополиморфные, с твоей точки зрения. А ListN!1 - это уже обычный тип.
Цитата MyNameIsIgor @
Компилятор же говорит, что надо сделать - http://ideone.com/iglyDq
Просто убрать public? Спасибо.

Автор: D_KEY 09.07.15, 12:40
Цитата DarkEld3r @
Но вообще как это (в варианте с дженериками) сделать без потери эффективности?

Без потери эффективности по сравнению с чем? С учетом того, что просто генерация это сделать не позволяет и даже на C++/D приходится стирать типы.

Добавлено
Цитата applegame @
Цитата D_KEY @
При вызове cons вызывается не первая перегрузка?
Она есть, но делается неявно компилятором.

И что?

Цитата
Цитата D_KEY @
А разве при передачи в main_ ListN!1 не кастуется к List!1?
Это частный случай. Точнее будет сказать: ListN!N кастуется к List!N - это полиморфные типы. Ну или псевдополиморфные, с твоей точки зрения. А ListN!1 - это уже обычный тип.

Так ты именно этот обычный тип и передаешь в main_. В первом же внутреннем вызове(и далее). В чем прикол-то?

Добавлено
Цитата MyNameIsIgor @
Когда остановить то эту генерацию?

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

Цитата
Или во время исполнения?

Наверное это не лучший вариант.

Я не знаю, думаю, что такие вещи надо как следует обдумать :)

Автор: applegame 09.07.15, 13:06
Цитата D_KEY @
Так ты именно этот обычный тип и передаешь в main_. В первом же внутреннем вызове(и далее). В чем прикол-то?
Скажем в том, что я вообще не манипулирую конкретными типами, только полиморфными вроде List!N. Обычные типы выводятся, кастуются и стираются сами. В первом варианте я стирал вручную. Во втором автоматически. Но автоматику все равно можно сломать если принудительно менять тип.

Ну вот, в жабе такая же байда. Ошибся и случайно скастовал к базовому классу (компилятор не возражает). И оно поломалось. Считается?
http://ideone.com/N8RiOn

Добавлено
Цитата applegame @
Ошибся и случайно скастовал к базовому классу
А базовый ли это класс? Но компилятор все равно не возражает :)

Автор: MyNameIsIgor 09.07.15, 13:35
Цитата applegame @
Ошибся и случайно скастовал к базовому классу

:facepalm: Ни к какому базовому классу вы ничего не кастовали.
Цитата applegame @
В первом варианте я стирал вручную. Во втором автоматически.

Угу. Оно само конструирует именно ListN!1, а не от выведенного числа. Оно само передаёт указатели на наследников туда, где ожидается указатель на Base. И вообще оно само всё написалось.
Цитата applegame @
Считается?

Ответьте сами. Если считается, то разговор закончен, можете залезать на белого коня - мне никакой выгоды от вашего просвещения.
Если не считается, то подумайте, что именно вы доказали на примере Java.

Автор: D_KEY 09.07.15, 13:46
Цитата applegame @
Ошибся и случайно скастовал к базовому классу (компилятор не возражает). И оно поломалось.

Цитата
Exception in thread "main" java.lang.ClassCastException: Nil cannot be cast to Cons

:-?

Автор: DarkEld3r 09.07.15, 13:50
Цитата D_KEY @
Без потери эффективности по сравнению с чем?
По сравнению с тупой генерацией. В джаве ведь, в итоге, в дженериках оказывался просто Object, так? Это уже менее эффективно, не говоря уже о том, что требует наличия общего родителя для всех типов.

Автор: D_KEY 09.07.15, 13:54
Цитата applegame @
Скажем в том, что я вообще не манипулирую конкретными типами

Явно, может и нет. А неявно происходит тоже самое, что сделал я. Не вижу разницы.

Цитата
Но автоматику все равно можно сломать если принудительно менять тип.

Я все делаю в рамках системы типов языка. Более того, я делают тоже самое, что неявно происходит в твоем коде.

Добавлено
Цитата DarkEld3r @
Цитата D_KEY @
Без потери эффективности по сравнению с чем?
По сравнению с тупой генерацией.

Тупой генерацией такие задачи не решить.

Цитата
В джаве ведь, в итоге, в дженериках оказывался просто Object, так? Это уже менее эффективно, не говоря уже о том, что требует наличия общего родителя для всех типов.

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

Автор: applegame 09.07.15, 13:57
Цитата MyNameIsIgor @
:facepalm: Ни к какому базовому классу вы ничего не кастовали.
Я уже все сам понял.
Цитата MyNameIsIgor @
Если не считается, то подумайте, что именно вы доказали на примере Java.
Честно говоря я жабу не знаю. И мне не ясно почему компилятор позволил кастовать к A. Например к Integer он не дает кастовать.

Добавлено
Цитата D_KEY @
:-?
Ну эксепшн то не там где я кастовал а уже при вычислении скалярного произведения. Это и сбивает с толку.

Автор: applegame 09.07.15, 14:05
То ли лыжи не едут, то ли жабовский код какой-то неполный. Как сгенерить список длинее одного элемента? Вот это не компилируется:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    _main(n-1, i+1, new Cons<A>(i, new Cons<A>(2*i+1,first)), new Cons<A>(i, new Cons<A>(i*i, second)));

http://ideone.com/RSGkSv
ЧЯДНТ?

Автор: MyNameIsIgor 09.07.15, 14:08
Цитата applegame @
Честно говоря я жабу не знаю. И мне не ясно почему компилятор позволил кастовать к A. Например к Integer он не дает кастовать.

Через (Integer)(Object) даст. Но тогда уже _main не вызовется.
Цитата applegame @
Ну эксепшн то не там где я кастовал а уже при вычислении скалярного произведения. Это и сбивает с толку.

Это просто говорит о том, что данный явный каст перенёс типизацию в run-time. Это примерно то же, что в плюсах запилить reinterpret_cast, а потом удивляться, что оно упало, но не там, где сам каст.

Добавлено
Цитата applegame @
ЧЯДНТ?

http://ideone.com/Huiqad. Естественно, ошибка несоответствия типов (длин списков).


Извиняюсь, слишком бегло посмотрел. Всё верно - http://ideone.com/RlfL3N

Автор: applegame 09.07.15, 14:15
В шарпе таже фигня :( http://ideone.com/LLsWQN

Автор: MyNameIsIgor 09.07.15, 14:16
Цитата applegame @
В шарпе таже фигня

Нужно создавать Cons<Cons<A>>. Вы не забыли, что тип показывает длину списка?

Автор: applegame 09.07.15, 14:22
Цитата MyNameIsIgor @
Нужно создавать Cons<Cons<A>>. Вы не забыли, что тип показывает длину списка?
Ага, список из трех элементов будет Cons<Cons<Cons<A>>.

Автор: D_KEY 09.07.15, 14:27
:facepalm:

Автор: applegame 09.07.15, 14:34
Цитата D_KEY @
:facepalm:
Что тебя так поразило?

Автор: D_KEY 09.07.15, 14:36
Да все :)
Я пока со стороны посмотрю.

Автор: applegame 09.07.15, 14:38
Цитата D_KEY @
Да все :)
Я пока со стороны посмотрю.
Ну а что ты хочешь? Я тороплюсь, на жабе отродясь не кодил.

Добавлено
Окай, с жабой я сфейлился. Но вот никак не могу достигнуть дзен. Все равно, какое-то ощущение, что что-то где-то не так.

Автор: MyNameIsIgor 09.07.15, 15:03
Цитата D_KEY @
Я пока со стороны посмотрю.

Вот это fail, йащитаю.

Автор: applegame 09.07.15, 16:47
Не выходит каменный цветок. Данная задача решается на D только с некоторыми допущениями (нескрываемое и поэтому легко ломаемое стирание типов). Считать ли эти допущения критическими для этой задачи или нет - пусть каждый решает сам для себя. На этом предлагаю данную тему закрыть до очередного зуда в заднице.

Автор: D_KEY 09.07.15, 19:53
Цитата applegame @
Не выходит каменный цветок. Данная задача решается на D только с некоторыми допущениями (нескрываемое и поэтому легко ломаемое стирание типов). Считать ли эти допущения критическими для этой задачи или нет - пусть каждый решает сам для себя.

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

Мы холивар о ПП тоже заканчиваем? Жаль, мне бы хотелось получить ответ на мои последние вопросы на этот счёт...

Автор: applegame 10.07.15, 06:32
Цитата D_KEY @
однако хорошо иллюстрирует разницу между реализациями параметрического полиморфизма
fixed.
Цитата D_KEY @
Мы холивар о ПП тоже заканчиваем? Жаль, мне бы хотелось получить ответ на мои последние вопросы на этот счёт...
Твои последние вопросы повторяются. Мы ходим по кругу. Давай лучше обобщим сказанное.

Все участвующие в споре знают как работают шаблоны. Но одна сторона считает это параметрическим полиморфизмом, а другая нет. Остается только аппелировать к общественному мнению. Чуть менее, чем во всех источниках декларируется, что C++ поддерживает параметрический полиморфизм. Если бы можно было организовать суд, то у меня есть веские основания считать, что ты бы его проиграл.

Автор: D_KEY 10.07.15, 07:23
Цитата applegame @
Твои последние вопросы повторяются. Мы ходим по кругу.

Эти я еще повторить не успел:
Цитата D_KEY @
Qraizer, applegame

Тут есть параметрический полиморфизм?

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    class A {
    public:
       virtual void foo()
       {
            // default implementation
       }
    };
     
    void bar(A & a)
    {
        a.foo();
    }


А если нет, то можете объяснить принципиальную разницу с шаблонами(в контексте нашего разговора, естественно)? Ведь ваш аргумент на счет "не специализируй" работает и тут :-?


Цитата
Чуть менее, чем во всех источниках декларируется, что C++ поддерживает параметрический полиморфизм.

Можно пример?

Автор: korvin 10.07.15, 08:44
Цитата applegame @
Что значит бессмысленнен? Он там просто есть.

Его там нет, потому что нет компайл-тайм типов. Убери из D/Java/C# шаблоны/дженерики и используй везде Object. Сравни дженерик-TList в делфе (параметризуется типом элементов) и TStringList (ничем не параметризуется, тип элемента задан жестко --- TObject).

Автор: applegame 10.07.15, 08:45
Цитата D_KEY @
Можно пример?
В гугле забанили?
English
Русский
Цитата Wikipedia:RU
Затем есть два способа реализации параметрического полиморфизма (в С++-сообществе обычно называемого «обобщённым программированием»). Первый способ унаследован из Си — использование бестипового указателя и приведение типа в зависимости от других данных — хотя в С++ этот способ традиционно считается неидеоматичным и опасным. Второй заключается в использовании шаблонов — но, в отличие от обычных реализаций параметрического полиморфизма, в С++ происходит автоматическая генерация семейства перегруженных мономорфных функций на основании полиморфного определения (шаблона) в соответствии с контекстами его использования — то есть параметрический полиморфизм на уровне исходного кода транслируется в ситуативный (ad hoc) на уровне машинного, за что С++ подвергается критике

Цитата Wikipedia:EN
Templates in C++ provide a sophisticated mechanism for writing generic, polymorphic code (i.e. parametric polymorphism). In particular, through the Curiously Recurring Template Pattern,


Добавлено
Цитата korvin @
Его там нет, потому что нет компайл-тайм типов.
Типы вообще тут не причем. Сишная функция printf параметрически полиморфна, как бы вам с D_KEY не хотелось обратного.

Автор: korvin 10.07.15, 08:49
Цитата applegame @
Еще раз читаем определение ПП, вдумчиво пожалуйста и ищем там хоть какое-то упоминание динамической/статической типизации, run-time или compile-time:

Рукипедия, она такая, да.

Цитата
In programming languages and type theory, parametric polymorphism is a way to make a language more expressive, while still maintaining full static type-safety.


Цитата
Parametric polymorphism was first introduced to programming languages in ML in 1975.

--- заметь, не в Лиспе 1958-го.

https://en.wikipedia.org/wiki/Parametric_polymorphism

Автор: applegame 10.07.15, 08:51
Цитата D_KEY @
А если нет, то можете объяснить принципиальную разницу с шаблонами(в контексте нашего разговора, естественно)?
Функция bar - полиморфна, так как может принимать не только тип A, но и всех его потомков.

Добавлено
Цитата korvin @
Рукипедия, она такая, да.
Переведи фразу на русский - зто не определение ПП.
Цитата korvin @
--- заметь, не в Лиспе 1958-го.
И что дальше? Введение обособленного понятия параметрического полиморфизма не нужно для динамически типизированных языков.

Автор: korvin 10.07.15, 09:09
Цитата OpenGL @
Не понял задачу. Как во время компиляции можно определить длину списка, которая известна только в рантайме? Имхо, задача поставлена некорректно.

Не проверить длину списка,а гарантировать, что у двух списков одинаковая длина. Рекомендую посмотреть пример на Agda и почитать описание как и почему оно работает.

Добавлено
Цитата applegame @
Типы вообще тут не причем. Сишная функция printf параметрически полиморфна, как бы вам с D_KEY не хотелось обратного.

:wacko: Лол, вся суть полиморфизма в работе с типами.

Цитата
In programming languages and type theory, polymorphism (from Greek πολύς, polys, "many, much" and μορφή, morphē, "form, shape") is the provision of a single interface to entities of different types.

--- https://en.wikipedia.org/wiki/Polymorphism_...mputer_science)

Про сишный printf --- клиника, там вообще нет типов и типобезопасности (для чего полиморфизм и нужен в т.ч., особенно параметрический).

Добавлено
Цитата
JavaScript is dynamically duck typed. Generics don't make sense in that context. There aren't static types. It being supported or not is nonsensical.

--- http://stackoverflow.com/questions/6697832...6697862#6697862

Цитата
Parametric Polymorphism is a way to define types or functions that are generic over other types. The genericity can be expressed by using type variables for the parameter type, and by a mechanism to explicitly or implicitly replace the type variables with concrete types when necessary.
Write a small example for a type declaration that is parametric over another type, together with a short bit of code (and its type signature) that uses it. A good example is a container type, let's say a binary tree, together with some function that traverses the tree, say, a map-function that operates on every element of the tree.
This language feature only applies to statically-typed languages.

--- http://rosettacode.org/wiki/Parametric_polymorphism

Автор: D_KEY 10.07.15, 09:48
Цитата applegame @
Цитата D_KEY @
А если нет, то можете объяснить принципиальную разницу с шаблонами(в контексте нашего разговора, естественно)?
Функция bar - полиморфна, так как может принимать не только тип A, но и всех его потомков.

Параметрически полиморфна?

Добавлено
Цитата applegame @
Сишная функция printf параметрически полиморфна, как бы вам с D_KEY не хотелось обратного.

Это какой-то догмат или что? Или я пропустил твою аргументацию?

Автор: applegame 10.07.15, 09:59
Ой, ладно, господа. Мне уже скучно на эту тему спорить. Если из задачки я кое что полезное для себя почерпнул, то из спора о ПП ничего ценного извлечь не удасться. Считайте что в плюсах нет ПП ради бога.
Давайте лучше поговорим о чем-нибудь более конструктивном. Тем более что у меня назревает вопросец.

Автор: korvin 10.07.15, 10:04
Цитата applegame @
Давайте лучше поговорим о чем-нибудь более конструктивном. Тем более что у меня назревает вопросец.

Так вбрасывай.

Автор: D_KEY 10.07.15, 10:11
Цитата applegame @
Давайте лучше поговорим о чем-нибудь более конструктивном.

Например?=) У тебя есть какие-то новые мысли на счет сабжа?

Цитата
Тем более что у меня назревает вопросец.

Ну как назреет, задавай :)

Автор: applegame 12.07.15, 08:24
Насчет жабы. D_KEY, как ты считаешь, Жаба поддерживает ПП?

Автор: D_KEY 12.07.15, 09:00
Цитата applegame @
Насчет жабы. D_KEY, как ты считаешь, Жаба поддерживает ПП?

Да, пока не вижу оснований считать, что не поддерживает.

Автор: applegame 12.07.15, 09:11
Цитата D_KEY @
Да, пока не вижу оснований считать, что не поддерживает.
Только вот странный там полиморфизм получается. Вот у нас два типа имеющих общий метод:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    class Foo {
      public String test() { return "Foo"; }
    }
     
    class Bar {
      public String test() { return "Bar"; }
    }

А вот это не компилируется:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    public static <T> String polymorph(T param) { return param.test(); }



Бида, печаль. В жабе для того что бы ПП-функция могла принимать несколько разных типов, мы вынуждены создать дополнительный тип с "общим" методом и от него унаследоваться. Но тогда получается, что наша ПП-функция де-факто принимает параметр одного типа:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    abstract class Base {
        abstract public String test();
    }
     
    class Foo extends Base {
        public String test() { return "Foo"; }
    }
     
    class Bar extends Base {
        public String test() { return "Bar"; }
    }

И тогда, вот это будет работать:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    public static <T extends Base> String test(T param) { return param.test(); }

Заметь приходяится явно указывать, что наш полиморфный тип на самом деле вполне конкретный тип - Base.

Ваши комментарии, D_KEY?

Автор: D_KEY 12.07.15, 10:03
Он не конкретный :) Ты просто задал требование к типам-параметрам. С точки зрения системы типов, если типы будут переданы разные, то и джегерик-тип будет разным, что и демонстрировал тот самый пример, не дающий тебе породить типы разной длины

Автор: applegame 12.07.15, 10:05
Цитата D_KEY @
Он не конкретный :) Ты просто задал требование к типам-параметрам. С точки зрения системы типов, если типы будут переданы разные, то и джегерик-тип будет разным, что и демонстрировал тот самый пример, не дающий тебе породить типы разной длины
Это конечно да, но что это за полиморфизм, заставляющий меня плодить общих предков для моих типов? Могу ли я создать в жабе ПП-функцию, без требований к типам параметрам Foo и Bar?
В run-time, кстати, это будет именно конкретный тип - Base. Я даже не смогу определить точный тип в run-time (в отличие от шарпа, например). Type-erasre он такой, да.

Автор: D_KEY 12.07.15, 10:17
Рантайм имеет весьма косвенное отношение к системе типов.

Цитата
Могу ли я создать в жабе ПП-функцию, без требований к типам параметрам Foo и Bar?

Можешь, если тебе не требуется вызывать их методы или ещё как-то опираться на информацию о них. А если нужно, то надо указать соответствующие требования. Все логично.

Автор: applegame 12.07.15, 10:38
Цитата D_KEY @
Рантайм имеет весьма косвенное отношение к системе типов.
К системе типов может быть и косвенное, а вот к ПП имеет вполне прямое отношение. Внутри моей функции оказался совсем не тот тип, который подавался на вход.
Цитата D_KEY @
А если нужно, то надо указать соответствующие требования. Все логично.
Нет, не логично. Зачем их указывать? Эти требования, а так же соответствие типов этим требованиям можно вывести из исходного кода, что успешно делают плюсы.
Да и что это за требования такие, требующие создания нового типа? А вдргу мои классы Foo и Bar торчат где-то в сторонней либе? Предка-то уже не создашь.

Автор: D_KEY 12.07.15, 10:41
Цитата applegame @
Цитата D_KEY @
Рантайм имеет весьма косвенное отношение к системе типов.
К системе типов может быть и косвенное, а вот к ПП имеет вполне прямое отношение.

ПП - это часть системы типов.

Автор: applegame 12.07.15, 10:46
Цитата D_KEY @
ПП - это часть системы типов.
Ага, отличная система типов, в которой ПП напрочь ломает run-time reflection. Система типов не должна делать вид, что у функции параметры одиного типа, а на самом деле они оказываются другого типа, без разницы в run-time или compile-time.
Ну и что насчет:
Цитата applegame @
Цитата D_KEY @
А если нужно, то надо указать соответствующие требования. Все логично.
Нет, не логично. Зачем их указывать? Эти требования, а так же соответствие типов этим требованиям можно вывести из исходного кода, что успешно делают плюсы.
Да и что это за требования такие, требующие создания нового типа? А вдргу мои классы Foo и Bar торчат где-то в сторонней либе? Предка-то уже не создашь.

Автор: D_KEY 12.07.15, 10:49
Цитата applegame @
Внутри моей функции оказался совсем не тот тип, который подавался на вход.

В статике - тот. С учётом того, что сама полиморфная функция от типов-параметров не зависит. Т.е. задача системы типов проверить корректность в статике. А что там в реализации - отдельная тема.

Добавлено
Цитата applegame @
Цитата D_KEY @
ПП - это часть системы типов.
Ага, отличная система типов, в которой ПП напрочь ломает run-time reflection.

Это вопросы к языку в целом, а не к системе типов. Я яву не люблю :) Но ПП в системе типов у них есть.

Добавлено
Цитата applegame @
Зачем их указывать?

Для обеспечения корректности с точки зрения системы типов.

Цитата
Эти требования, а так же соответствие типов этим требованиям можно вывести из исходного кода


Можно. Но Ява не умеет. И возможность явного указания таки является полезной в любом случае.

Цитата
что успешно делают плюсы.

Не делают. Плюсы ничего не выводят. Там просто попытка инстанцирования и простыни ошибок в случае фейла(ну sfinae и пр. тоже).

Цитата
А вдргу мои классы Foo и Bar торчат где-то в сторонней либе? Предка-то уже не создашь.

Да, система типов в яве убогая. Зато в твоём распоряжении есть всякие паттерны проектирования, которые ты можешь заюзать в данном случае :D

Добавлено
Цитата TAPL
Система типов - это гибко управляемый синтаксический метод доказательства отсутствия в программе определенных видов поведения при помощи классификации выражений языка по разновидностям вычисляемых ими значений

Автор: applegame 12.07.15, 11:11
Цитата D_KEY @
Не делают. Плюсы ничего не выводят.
Ну как же? Код аналогичный жабовскому в первом примере скомпилируется.
Цитата D_KEY @
Система типов - это гибко управляемый синтаксический метод доказательства отсутствия в программе определенных видов поведения при помощи классификации выражений языка по разновидностям вычисляемых ими значений
И в жабе она лажает. В этом коде:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    class Foo {
      public String test() { return "Foo"; }
    }
     
    class Bar {
      public String test() { return "Bar"; }
    }
     
    class Test {
      public static <T> String polymorph(T param) { return param.test(); }
      public static void main(String[] args) {
        polymorph(new Foo());
      }
    }
она мне доказывает, что я не могу выполнить метод test для объекта с типом Foo, что является ложью. А вот система типов C++/D с такой задачей справляется.

Автор: Da$aD 12.07.15, 13:16
Цитата applegame @
она мне доказывает, что я не могу выполнить метод test для объекта с типом Foo, что является ложью. А вот система типов C++/D с такой задачей справляется.

Этот пример наглядно демонстрирует подход "Со своим уставом в чужой монастырь". Это, по факту, копипаста из C++, которая внезапно не работает в другом языке.

Автор: applegame 12.07.15, 13:42
Цитата Da$aD @
Этот пример наглядно демонстрирует подход "Со своим уставом в чужой монастырь". Это, по факту, копипаста из C++, которая внезапно не работает в другом языке.
A этот пример наглядно демонстрирует, что вы не понимаете о чем идет речь. А речь идет явно не о C++ vs Java.

Автор: applegame 12.07.15, 15:08
Кажется я достиг дзен. Если заблуждаюсь поправьте.
Итак, рассуждения в контексте Java:
Долго думал как оно вообще работает. Дело в том, что просто стирания типов явно недостаточно для компиляции кода той самой задачи.
Стирание типов позволяет нам создать один-единственный экземпляр функции, работающий с, грубо говоря, void*. Но что делать с бесконечной генерацией типов, которая, казалось бы необходима для проверки типов compile-time? И вот тут, как я полагаю, происходит самое интересное: компилятор определяет, что доказать правильность использования этой бесконечной последовательности типов (допустим A<A<A<A....) можно доказав утверждение: если для любого типа T верен тип A<T>, то также будет верент тип A<A<T>>. Тогда можно не разворачивать в бесконечность типы - все равно в run-time все сведется к void*.
Подозреваю, что в каких-то случаях компилятор жабы может лажать и таки ударяться в бесконечное разворачивание. Но не буду утвержать.

Что касается C++/D, то такую оптимизацию мы делать не можем из-за требования разворачивать типы до упора.

Автор: korvin 12.07.15, 18:34
Цитата applegame @
Бида, печаль. В жабе для того что бы ПП-функция могла принимать несколько разных типов, мы вынуждены создать дополнительный тип с "общим" методом и от него унаследоваться. Но тогда получается, что наша ПП-функция де-факто принимает параметр одного типа:

На самом деле бида-пичаль в том, что в твоём примере нет никакого ПП.

Твой пример почти полностью соответствует этому.

Никакого ПП здесь нет.

Вот это ПП: Haskell, Java.

Конечно в джаве не всё так гладко, но ты можешь взять Scala, например.

Автор: applegame 12.07.15, 19:10
Цитата korvin @
На самом деле бида-пичаль в том, что в твоём примере нет никакого ПП.
До-о-о. Бида и пичаль, что и в Haskell невозможно обойтись без лишнего типа. В твоем варианте - класс Test, которым ты объединил Foo и Bar. К счастью в Haskell - это делается куда изящнее чем в жабе.

Автор: korvin 12.07.15, 19:13
Цитата applegame @
До-о-о. Бида и пичаль, что и в Haskell невозможно обойтись без лишнего типа. В твоем варианте - класс Test, которым ты объединил Foo и Bar. К счастью в Haskell - это делается куда изящнее чем в жабе.

Какого "лишнего типа"? Во-первых, там нет никакого лишнего типа, во-вторых, ты, похоже, вообще не понимаешь, что такое ПП.

Автор: D_KEY 12.07.15, 19:28
Цитата applegame @
Цитата korvin @
На самом деле бида-пичаль в том, что в твоём примере нет никакого ПП.
До-о-о. Бида и пичаль, что и в Haskell невозможно обойтись без лишнего типа. В твоем варианте - класс Test, которым ты объединил Foo и Bar. К счастью в Haskell - это делается куда изящнее чем в жабе.

class в haskell - это тайпкласс, а не тип.

Автор: applegame 12.07.15, 19:36
Цитата D_KEY @
class в haskell - это тайпкласс, а не тип.
Как говориться, тот же тип - вид сбоку. Похоже на traits из Rust, который тоже, как бы не тип :)

Добавлено
Цитата korvin @
Вот это ПП: Haskell, Java.

Но, как только мы пишем аналог на D, то ПП магически исчезает. :)

Автор: korvin 12.07.15, 19:46
Цитата applegame @
Цитата D_KEY @
class в haskell - это тайпкласс, а не тип.
Как говориться, тот же тип - вид сбоку.

:facepalm: Ты про первый пример что ли? Я же тебе написал, что там фактически нет ПП.

Автор: D_KEY 12.07.15, 19:47
Цитата applegame @
Цитата D_KEY @
Не делают. Плюсы ничего не выводят.
Ну как же? Код аналогичный жабовскому в первом примере скомпилируется.

Скомпилируется, потому, что сгенерированный код не будет содержать ошибок. Это не значит, что плюсы что-то там выводят :)

Цитата
она мне доказывает, что я не могу выполнить метод test для объекта с типом Foo, что является ложью.

Ты читаешь сообщения компилятора?
Ты не можешь выполнить метод test для объекта типовой переменной T, потому, что тебе о типе ничего неизвестно(в случае чистого ПП и не должно). И именно об этом тебе скажет компилятор. Ругаться он будет и без Foo и Bar. Они в твоём примере не нужны.

Ты мешаешь в одну кучу ПП и специальный полиморфизм.

Автор: applegame 12.07.15, 19:49
Цитата korvin @
Какого "лишнего типа"? Во-первых, там нет никакого лишнего типа,
Пардон, попутал примеры.

Автор: korvin 12.07.15, 19:52
Цитата applegame @
Но, как только мы пишем аналог на D, то ПП магически исчезает. :)

Куда? Пропиши все типы явно, без auto и alias. А то я тоже могу в исходнике на Хаскелле убрать все типы, но мы тут не вывод типов обсуждаем.

Автор: D_KEY 12.07.15, 19:52
Цитата applegame @
Как говориться, тот же тип - вид сбоку.

Нет. Это тайпкласс :)

Цитата
Похоже на traits из Rust, который тоже, как бы не тип :)

Скорее похоже на плюсовые концепты, которые не типы.
trait в Rust вполне тип.

Цитата
Но, как только мы пишем аналог на D, то ПП магически исчезает. :)

Это имитация. Я же могу специализировать transform? Значит это вид специального полиморфизма, просто конкретно в твоём случае ты используешь только реализацию по умолчанию.

Автор: applegame 12.07.15, 19:57
Цитата D_KEY @
Ты не можешь выполнить метод test для объекта типовой переменной T, потому, что тебе о типе ничего неизвестно(в случае чистого ПП и не должно).
Все верно. Но тип знать и не требуется. Требуется знать, поддерживает тип какую-либо операцию или нет.

Добавлено
Цитата korvin @
Куда?
Откуда, я знаю. Спроси у D_KEY. :)
Цитата korvin @
Пропиши все типы явно, без auto и alias. А то я тоже могу в исходнике на Хаскелле убрать все типы, но мы тут не вывод типов обсуждаем.
alias и auto просто для удобства. Они роли не играют.

Добавлено
Цитата D_KEY @
Я же могу специализировать transform? Значит это вид специального полиморфизма
А можешь и не специализировать, тогда это не будет видом специального полиморфизма :).

Автор: korvin 12.07.15, 20:05
Цитата applegame @
По D_KEY исчезают. alias и auto просто для удобства. Они роли не играют.

Я не знаю, что там в D, но D_KEY пишет о том, что плюсовый шаблон может иметь специализацию, отличную от обобщенного варианта (перегрузку, в некотором смысле), например:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template<typename T>
    int length(vector<T> xs) { return xs.size(); }
     
    template<>
    int length(vector<int> xs) { return 1234; }


т.е. для разных типов функция length показывает разное поведение, что является ad-hoc полиморфизмом.

ПП-функция же имеет всегда одинаковое поведение для любых типов.

Автор: applegame 12.07.15, 20:18
Цитата korvin @
Я не знаю, что там в D, но D_KEY пишет о том, что плюсовый шаблон может иметь специализацию, отличную от обобщенного варианта (перегрузку, в некотором смысле), например:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template<typename T>
    int length(vector<T> xs) { return xs.size(); }
     
    template<>
    int length(vector<int> xs) { return 1234; }


т.е. для разных типов функция length показывает разное поведение, что является ad-hoc полиморфизмом.

ПП-функция же имеет всегда одинаковое поведение для любых типов.

Тут у меня нет возражений. Если шаблон специализирован, то тут и правда специальный полиморфизм. Но а если шаблон не специализирован?
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template<typename T>
    int length(T xs) { return xs.size(); }

Это ПП или нет?

Автор: D_KEY 12.07.15, 20:18
Цитата korvin @
Цитата applegame @
По D_KEY исчезают. alias и auto просто для удобства. Они роли не играют.

Я не знаю, что там в D, но D_KEY пишет о том, что плюсовый шаблон может иметь специализацию, отличную от обобщенного варианта (перегрузку, в некотором смысле), например:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template<typename T>
    int length(vector<T> xs) { return xs.size(); }
     
    template<>
    int length(vector<int> xs) { return 1234; }


т.е. для разных типов функция length показывает разное поведение, что является ad-hoc полиморфизмом.

ПП-функция же имеет всегда одинаковое поведение для любых типов.

Именно так.

Добавлено
Цитата applegame @
Если шаблон специализирован, то тут и правда специальный полиморфизм.

Специализировать его может любой желающий в любой момент времени. Т.е. сам механизм является средством специального полиморфизма.

Добавлено
Цитата applegame @
Но а если шаблон не специализирован?

А если не переопределение виртуальная функция? А если не переопределена дефолтная реализация функции в тайпклассе? Это неважно. Сам механизм остаётся средством специального полиморфизма во всех перечисленных случаях.

Автор: korvin 12.07.15, 20:23
Цитата applegame @
Тут у меня нет возражений. Если шаблон специализирован, то тут и правда ad-hoc полиморфизм. Но а если шаблон не специализирован?

А если метод не перекрывать, значит ли это, что он не перекрываем? Если публичный метод не вызывать нигде извне объекта, значит ли это, что он приватный?

Автор: D_KEY 12.07.15, 20:24
И именно тот факт, что шаблоны являются средством специального полиморфизма и мешает решить ту задачу на C++.

Добавлено
Меня, честно говоря, больше всего удивляет тут Rust. Возможно я чего-то не понимаю, но Rust должен справляться с этой задачей, не смотря на генерацию в реализации.

Автор: korvin 12.07.15, 20:26
Обобщённый шаблон --- это как базовый класс с дефолтной реализацией методов. Ты можешь не наследоваться от него и не переопределять методы, но классом он быть не перестаёт и структурой от этого не становится.

Автор: applegame 12.07.15, 20:33
Цитата korvin @
А если метод не перекрывать, значит ли это, что он не перекрываем?
А сама возможность перекрыть метод говорит о том, что метод параметрически не полиморфный?

Автор: korvin 12.07.15, 20:37
Цитата applegame @
А сама возможность перекрыть метод говорит о том, что метод параметрически не полиморфный?

Это была параллельная аналогия.

Утверждение "Обобщённый шаблон можно не перегружать, следовательно шаблоны --- это ПП." равносильно
утверждению "Базовый класс можно не наследовать, следовательно классы --- это структуры."

Автор: applegame 12.07.15, 20:40
Цитата korvin @
Это была параллельная аналогия.
Ну а следует ли из того, что обобщенный шаблон можно перегружать, что он --- это не ПП?

Автор: korvin 12.07.15, 20:42
Цитата applegame @
Ну а следует ли из того, что обобщенный шаблон можно перегружать, что он --- это не ПП?

:facepalm: что за демагогия? Следует ли из того, что шаблон можно перегружать, что Земля плоская?

Автор: applegame 12.07.15, 20:44
Цитата korvin @
:facepalm: что за демагогия? Следует ли из того, что шаблон можно перегружать, что Земля плоская?
Я пытаюсь понять твое мнение. Задам вопрос по другому:
Может ли некий частный случай обобщенного шаблона считаться ПП?

Добавлено
Ведь и в Хаскелле можно в некотором роде "специализировать" уже написанные полиморфные функции.
Например для того чтобы мой тип Foo мог участвовать в операциях сложения мне придется написать инстанс Num для Foo, в котором специализировать сложение для этого самомго Foo. Ведь так?

Автор: D_KEY 12.07.15, 20:49
Цитата applegame @
Может ли некий частный случай обобщенного шаблона считаться ПП?

Я вот твоего вопроса не понимаю. Есть механизм шаблонов. Он относится к механизмам специального полиморфизма.

Добавлено
Цитата applegame @
Например для того чтобы мой тип Foo мог участвовать в операциях сложения мне придется написать инстанс Num для Foo, в котором специализировать сложение для этого самомго Foo. Так?

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

Автор: applegame 12.07.15, 20:53
Цитата D_KEY @
Да, тайпклассы - это вид специального полиморфизма.
Тогда получается, что операция сложения в Хаскелле - параметрически не полиморфна? Или она становится не полиморфной как только я специализирую ее для определенного нового типа?

Автор: D_KEY 12.07.15, 20:55
Цитата applegame @
Цитата D_KEY @
Да, тайпклассы - это вид специального полиморфизма.
Тогда получается, что операция сложения в Хаскелле - параметрически не полиморфна? Или она становится не полиморфной как только я специализирую ее для определенного нового типа?

Да, это специальный полиморфизм. И не только в haskell, но и в C++, когда ты задаешь operator + для своего типа. Перегрузка функций - тоже специальный полиморфизм.

Автор: applegame 12.07.15, 20:57
Цитата D_KEY @
Меня, честно говоря, больше всего удивляет тут Rust. Возможно я чего-то не понимаю, но Rust должен справляться с этой задачей, не смотря на генерацию в реализации.
Я думаю, генерация функций в этом деле не играет роли. Ведь важно проверить типы. И как это сделать не разворачивая рекурсию типов до упора?

Добавлено
Цитата D_KEY @
Да, это специальный полиморфизм. И не только в haskell, но и в C++, когда ты задаешь operator + для своего типа. Перегрузка функций - тоже специальный полиморфизм.
Дык я могу любую функцию в Haskell специализировать, не только сложение. И что эти функции от этого перестают быть параметрически полиморфными? Тогда не понятно, чем тебе мешает возможность специализировать шаблоны?

Добавлено
Хотя нет не любую. Только те, которые в тайп-классах.

Добавлено
Цитата D_KEY @
Перегрузка функций - тоже специальный полиморфизм.
Это очевидно. Так же как и виртуальные функции.

Автор: korvin 12.07.15, 21:12
Цитата applegame @
Я пытаюсь понять твое мнение. Задам вопрос по другому:
Может ли некий частный случай обобщенного шаблона считаться ПП?

Слово "частный" уже отвечает на вопрос.

Моё мнение --- шаблоны являются средством обобщённого программирования, но это другой механизм, нежели ПП.

Автор: applegame 12.07.15, 21:16
Ладно. Мы опять скатились не туда куда нужно. Я пытался понять как работают дженерики в Java и как выполнить тайп-чек рекурсивной функции-дженерика без бесконечного раскрытия типов?

Автор: korvin 12.07.15, 21:16
Цитата applegame @
Ведь и в Хаскелле можно в некотором роде "специализировать" уже написанные полиморфные функции.

Нельзя. Попробуй специализировать
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    map :: (a -> b) -> [a] -> [b]


Цитата applegame @
Например для того чтобы мой тип Foo мог участвовать в операциях сложения мне придется написать инстанс Num для Foo, в котором специализировать сложение для этого самомго Foo. Ведь так?

Так, и это не имеет отношения к ПП. Это два разных механизма. Также как ПП и классы в Ocaml, F#, Scala, например.

Автор: D_KEY 12.07.15, 21:16
Цитата applegame @
И как это сделать не разворачивая рекурсию типов до упора?

Так же, как в java, c# и haskell.

Цитата
Хотя нет не любую. Только те, которые в тайп-классах.

Именно так, да. И это специальный полиморфизм.

Добавлено
Цитата D_KEY @
Цитата applegame @
И как это сделать не разворачивая рекурсию типов до упора?

Так же, как в java, c# и haskell.

Как и в этих языках, в Rust дженерики являются средством ПП. И в rust нет перегрузки функций и специализации дженерик функций/типов.

Автор: applegame 12.07.15, 21:38
Интересное кино. Читаем на https://wiki.haskell.org/Polymorphism:
Цитата
Parametric polymorphism refers to when the type of a value contains one or more (unconstrained) type variables, so that the value may adopt any type that results from substituting those variables with concrete types.
In Haskell, this means any type in which a type variable, denoted by a name in a type beginning with a lowercase letter, appears without constraints (i.e. does not appear to the left of a =>).

Смотрим на код задачи:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    module Test where
    data Nil = Nil
    data Cons a = Cons Integer a
    class ScalarProduct a where scalarProduct :: a -> a -> Integer
    instance ScalarProduct Nil where scalarProduct Nil Nil = 0
    instance ScalarProduct a => ScalarProduct (Cons a) where scalarProduct (Cons n1 a1) (Cons n2 a2) = n1 * n2 + scalarProduct a1 a2
    main :: Integer -> Integer
    main n = main' n 0 Nil Nil where
      main' :: ScalarProduct a => Integer -> Integer -> a -> a -> Integer
      main' 0 _ as bs = scalarProduct as bs
      main' n i as bs = main' (n-1) (i+1) (Cons (2*i+1) as) (Cons (i^2) bs)
Таки получается, что функция main' - не полиморфна, так как есть ограничение ScalarProduct a.

Добавлено
И еще вот. Паттерн-матчинг в Хаскеле - это ad-hoc полиморфизм?

Автор: korvin 12.07.15, 22:04
Цитата applegame @
Таки получается, что функция main' - не полиморфна, так как есть ограничение ScalarProduct a.

Не параметрически полиморфна, да. Я тебе это сказал ещё про твой первый пример.

Цитата applegame @
И еще вот. Паттерн-матчинг в Хаскеле - это ad-hoc полиморфизм?

Паттерн-матчинг вообще никакого отношения к какому бы то ни было полиморфизму не имеет, это (почти) обычный switch/case.

Автор: applegame 12.07.15, 22:09
Цитата korvin @
Паттерн-матчинг вообще никакого отношения к какому бы то ни было полиморфизму не имеет, это (почти) обычный switch/case.
По типу паттерн-матчинга не бывает?

Добавлено
Вернемся к жабе. Как там разруливается полиморфная рекурсия и возможна ли реализация такой возможности в C++?

Автор: korvin 12.07.15, 22:13
Цитата applegame @
По типу паттерн-матчинга не бывает?

Нет.

Добавлено
Цитата applegame @
полиморфная рекурсия

Что это?

Автор: applegame 12.07.15, 22:16
Цитата korvin @
Что это?
- https://en.wikipedia.org/wiki/Polymorphic_recursion
То самое обо что спотыкаются решения задачи на C++/D/Rust.

Автор: korvin 12.07.15, 22:40
Java.

Автор: applegame 12.07.15, 22:58
D даже сам тип Nested не осиливает :D

Автор: DarkEld3r 12.07.15, 23:29
Цитата D_KEY @
trait в Rust вполне тип.
Тут можно поспорить. Как по мне, то растовые трейты просто выступают одновременно и "тайпклассами" и "интерфейсами".

Цитата D_KEY @
Меня, честно говоря, больше всего удивляет тут Rust. Возможно я чего-то не понимаю, но Rust должен справляться с этой задачей, не смотря на генерацию в реализации.
Каким образом?

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

Автор: Qraizer 13.07.15, 01:59
Я бы не очень доверял этой информации касательно выводов.
Цитата
The key disadvantage of monomorphizing code is that destroys separate compilation. In particular, it's impossible to compile a polymorphic library (say, the list library) separate from the clients.
Экспорт шаблонов, т.е. модель разделения, был реализован в Плюсах самым первым, т.е. до модели включения, что и послужило основной причиной оставить его в C++98. Но даже и без экспорта создание библиотек более чем возможно. Только нафик не нужно, ибо жутко неудобно.

Автор: applegame 13.07.15, 06:19
Цитата DarkEld3r @
Вот ещё информация по теме.

Весьма интересная информация. Авторы этого документа считают, что мономорфизация - это реализация полиморфизма, со своими преимуществами и недостатками. И, внезапно, MLton - компилятор Standard ML тоже использует мономорфизацию. И, насколько я понял, Standard ML не поддерживает полиморфную рекурсию.

Добавлено
Цитата Qraizer @
Экспорт шаблонов, т.е. модель разделения, был реализован в Плюсах самым первым, т.е. до модели включения, что и послужило основной причиной оставить его в C++98. Но даже и без экспорта создание библиотек более чем возможно. Только нафик не нужно, ибо жутко неудобно.
Ну это все же полумера. Экспортировать шаблон целиком никак нельзя, только конечное число его специализаций.

Автор: D_KEY 13.07.15, 07:54
Цитата applegame @
D даже сам тип Nested не осиливает :D

А ты чего ожидал?

Добавлено
Цитата DarkEld3r @
Цитата D_KEY @
trait в Rust вполне тип.
Тут можно поспорить. Как по мне, то растовые трейты просто выступают одновременно и "тайпклассами" и "интерфейсами".

Это интерфейсы(которые в Rust типы), которые можно использовать для ограничения дженериков(как и в Java, например).

Добавлено
Цитата DarkEld3r @
Цитата D_KEY @
Меня, честно говоря, больше всего удивляет тут Rust. Возможно я чего-то не понимаю, но Rust должен справляться с этой задачей, не смотря на генерацию в реализации.
Каким образом?

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

Автор: applegame 13.07.15, 09:59
Цитата D_KEY @
А ты чего ожидал?
На самом деле поддержку таких типов (по крайней мере классов) мржно реализовать не ломая текущие спеки. MLton такое осиливает путем ленивого разворачивания шаблонов. В D можно было бы разворачивать шаблон класса, только если этот тип реально используется в программе.
Цитата D_KEY @
Поскольку сами правила типизации в языке не содержат ничего, что бы заставляло инстанцировать типы бесконечно.
Да вроде как DarkEld3r говорил, что содержат. Тем не менее Rust вполне себе параметрически полиморфен, как и C++/D. А эта ваша полиморфная рекурсия, на самом деле, никому не сдалась.

Автор: D_KEY 13.07.15, 10:09
Цитата applegame @
Цитата D_KEY @
Поскольку сами правила типизации в языке не содержат ничего, что бы заставляло инстанцировать типы бесконечно.
Да вроде как DarkEld3r говорил, что содержат.

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

Автор: Qraizer 13.07.15, 10:55
Цитата applegame @
Экспортировать шаблон целиком никак нельзя, только конечное число его специализаций.
Любое можно. И кстати, не следует путать поддержку экспорта шаблонов и возможность не предоставлять сырцы для пользователя шаблона. Это разные аспекты, друг от друга не зависящие.

Автор: DarkEld3r 13.07.15, 11:36
Цитата D_KEY @
Подобные рекурсивные штуки в принципе, ни в каком из языков, не смогут использовать данные на стеке, то нам все равно понадобится куча и хранить мы так или иначе будем ссылки, а значит можно обойтись без генерации инстансов.
Да, но придётся вводить отдельные правила в каком случае генерить, а в каком нет. Опять же, нужно оно не так уж часто, так что думаю, что эту проблему будут игнорировать. Всё-таки в языке ещё многого более полезного нет.

Цитата D_KEY @
Специализации нет
Специализация обсуждалась и "планировалась". Я не сильно слежу за такими обсуждениями, так что не знаю насколько она приоритетна, но появиться вполне может.

Автор: D_KEY 13.07.15, 11:42
Цитата DarkEld3r @
Цитата D_KEY @
Подобные рекурсивные штуки в принципе, ни в каком из языков, не смогут использовать данные на стеке, то нам все равно понадобится куча и хранить мы так или иначе будем ссылки, а значит можно обойтись без генерации инстансов.
Да, но придётся вводить отдельные правила в каком случае генерить, а в каком нет. Опять же, нужно оно не так уж часто, так что думаю, что эту проблему будут игнорировать. Всё-таки в языке ещё многого более полезного нет.

Цитата D_KEY @
Специализации нет
Специализация обсуждалась и "планировалась". Я не сильно слежу за такими обсуждениями, так что не знаю насколько она приоритетна, но появиться вполне может.

В общем, язык пока еще слишком нестабилен для такого рода решений...

Автор: DarkEld3r 13.07.15, 12:23
Цитата D_KEY @
В общем, язык пока еще слишком нестабилен для такого рода решений...
Смотря что под "стабильностью" понимать. Обратную совместимость ломать очень сильно не хотят, отчасти, из-за этого многое и происходит медленнее, чем могло бы. В этом плане всё должно быть стабильно.

А вот в плане фич - действительно хотят сделать много всего ещё. Просто ресурсов не так уж много, насколько я понимаю. Опять же, нет какого-нибудь "комитета по стандартизации", в итоге между предложением и реализацией может проходить не так много времени - это тоже влияет на то, что планирующиеся возможности не освещаются как следует.

"Зато" релизы часто. Посмотрим, что из этого подхода вырастет.

Автор: korvin 13.07.15, 15:59
Цитата applegame @
Весьма интересная информация. Авторы этого документа считают, что мономорфизация - это реализация полиморфизма, со своими преимуществами и недостатками. И, внезапно, MLton - компилятор Standard ML тоже использует мономорфизацию. И, насколько я понял, Standard ML не поддерживает полиморфную рекурсию.

Цитата
The polymorphism in ML is not, however, exactly like the polymorphism in System F. ML restricts what
types a type variable may be instantiated with. Specifically, type variables can not be instantiated with
polymorphic types. Also, polymorphic types are not allowed to appear on the left-hand side of arrows (i.e.,
a polymorphic type cannot be the type of a function argument). This form of polymorphism is known as
let-polymorphism (due to the special role played by let in ML), or prenex polymorphism. These restrictions
ensure that type inference is possible.

--- http://www.seas.harvard.edu/courses/cs152/...tures/lec17.pdf

Ocaml тоже. Точнее Тип определить можно (как и в SML), а написать функцию length для него нельзя. (ideone подсвечивает символ length как "встроенный", поэтому переименовал в len)

Добавлено
Последнее предложение ("These restrictions
ensure that type inference is possible.") звучит странно, учитывая, что в Хаскелле и пример с полиморфной рекурсией работает и вывод типов.

Автор: applegame 13.07.15, 16:31
Цитата korvin @

Таки удалось реализовать это на D. Пришлось немного вручную постирать типы внутри Nested, но сам класс снаружи выглядит не хуже жабовского, все типы сохранены.
Благодаря затемплейченым свойствам и конструктору реальное иснтанцирование происходит только при собственно конструировании или обращению к свойству, как в MLton.
http://dpaste.dzfl.pl/b9c6a2a958e5

Добавлено
Цитата korvin @
Цитата
The polymorphism in ML is not, however, exactly like the polymorphism in System F. ML restricts what
types a type variable may be instantiated with. Specifically, type variables can not be instantiated with
polymorphic types. Also, polymorphic types are not allowed to appear on the left-hand side of arrows (i.e.,
a polymorphic type cannot be the type of a function argument). This form of polymorphism is known as
let-polymorphism (due to the special role played by let in ML), or prenex polymorphism. These restrictions
ensure that type inference is possible.

--- http://www.seas.harvard.edu/courses/cs152/...tures/lec17.pdf

Ocaml тоже. Точнее Тип определить можно (как и в SML), а написать функцию length для него нельзя. (ideone подсвечивает символ length как "встроенный", поэтому переименовал в len)

То есть они считают, что ПП в ML и Ocaml есть, но отличается от такового в Haskell. Как они там его обозвали? let-polymorphism или "prenex polymorphism", он же Rank-1 polymorphism. По-видимому такой же полиморфизм и во всяких C++/D.

Автор: D_KEY 13.07.15, 16:48
Цитата applegame @
По-видимому такой же полиморфизм и во всяких C++/D.

С чего он такой же, если дает возможность специализировать?

Автор: applegame 13.07.15, 16:51
Цитата D_KEY @
С чего он такой же, если дает возможность специализировать?
Да-да, я в курсе твоих фантазий, но что-то мало тех, кто с тобой согласен. Я бы даже сказал исчезающе мало.

Автор: D_KEY 13.07.15, 16:54
Цитата applegame @
Цитата D_KEY @
С чего он такой же, если дает возможность специализировать?
Да-да, я в курсе твоих фантазий, но что-то мало тех, кто с тобой согласен. Я бы даже сказал исчезающе мало.

Сильный аргумент. И да, с чего ты взял?

Добавлено
Кстати, а что именно является моими фантазиями? Что C++/D дает возможность специализировать? Или что Ocaml/ML не дают?

Автор: korvin 13.07.15, 17:00
Цитата applegame @
Да-да, я в курсе твоих фантазий, но что-то мало тех, кто с тобой согласен. Я бы даже сказал исчезающе мало.

Какие фантазии? Всё реально.

То что в ML переменная-тип не может быть сама параметризованной говорит лишь о некотором ограничении в реализации, хотя параметричность это не отменяет. Ну так а что ты хочешь? ML тянется с 77-го года, с тех пор много воды утекло, придумали Haskell в т.ч., но современные реализации ML тянут наследие прошлых лет, как C++ тянет наследие C, а D тянет наследие C++.

В Фортране, насколько я знаю, массивы не могут быть перекрывающимися(в смысле областей памяти), это ж не значит, что они перестали быть массивами.

Автор: applegame 13.07.15, 17:00
Цитата D_KEY @
Сильный аргумент. И да, с чего ты взял?
Ну сначала ты все твердил про инстанцирование, теперь вот оказалось, что ML, тоже оказывается инстанцирует, и задачка на нем не решается. И на Ocaml не решается. Я уже понял, по-твоему все что не Haskell и не Java с C# - это не ПП. :)

Добавлено
Цитата korvin @
Какие фантазии? Всё реально.
Что реально? Что в C++ нет параметрического полиморфизма? Ну фантазируйте с D_KEY на пару. Я вам мешать не буду :)

Автор: korvin 13.07.15, 17:06
Цитата applegame @
Что реально? Что в C++ нет параметрического полиморфизма?

Так же реально, как и то, что в C нет классов. Как в D нет мультиметодов.

Добавлено
Цитата applegame @
Ну сначала ты все твердил про инстанцирование, теперь вот оказалось, что ML, тоже оказывается инстанцирует, и задачка на нем не решается. И на Ocaml не решается. Я уже понял, по-твоему все что не Haskell и не Java с C# - это не ПП. :)

А ты любитель перевирать слова, я смотрю. D_KEY тебе совсем не об этом говорил. Более того:

1) он сам говорил, что это детали реализации.
2) он говорил не про само инстанциирование как реализацию, а про возможность переопределить правило инстанциирования для конкретных типов, что противоречит ПП.
3) проблема ML тут не в реализации, вообще-то, а в правилах типизации.

Автор: applegame 13.07.15, 17:15
Цитата korvin @
А ты любитель перевирать слова, я смотрю. D_KEY тебе совсем не об этом говорил. Более того:

1) он сам говорил, что это детали реализации.
2) он говорил не про само инстанциирование как реализацию, а про возможность переопределить правило инстанциирования для конкретных типов, что противоречит ПП.
Может тебе ссылки на его посты дать? Про детали реализации говорил я, а он как раз говорил, что истанцирование типов - это причина отрицать ПП в плюсах. Будешь мне тут рассказывать.

Добавлено
Вот:
Цитата D_KEY @
Тут принципиальная разница как раз в том, что в C++(и в D?) у нас нет полиморфных типов/функций, у нас есть механизм генерации конкретных типов и функций в зависимости от параметров.
И это разница не в реализации.

И вот тут втирают про генерацию активно:
trait + impl vs class (сообщение #3611075)
А тут он признал что ПП таки в плюсах есть, но потом отмазался, что он имел что-то совсем другое:
Цитата D_KEY @
Параметрический полиморфизм есть в C++(признаю таки), но там нет полиморфных типов.


Ты там тоже участвовал и все уже забыл. Ну молодцы, чо. И кто тут туперь мастер перевирать слова.

Автор: Qraizer 13.07.15, 17:24
Ни в одном (попадавшемся мне) источнике не определяется, как именно должен быть реализован полиморфизм, чтобы не потерять своё название. Главное, чтобы имело место полиморфное поведение, не противоречащее определению. Трансляция ПП в АдХок, например, на вики и вроде бы даже по ссылке DarkEld3r, не читал её всю настолько внимательно, рассматривается как предпочтительный вариант по ряду причин, хоть и не лишённый недостатков.
По теме задачи. Математика умеет работать с бесконечностями. Ряды, последовательности, бесконечно точные вещественные, итп. Практические применения математических абстракций далеко не всегда могут быть реализованы в полной мере, и это не открытие Америки. Рекурсия должна быть остановлена, итерации ограничены итп. Как именно это делается, неважно. В Плюсах, например, используется АдХок специализация, вводимая программистом явно, в Стандарте именуемая explicit specialization. В Шарпах это реализуется инстанцированием в run-time вкупе с необязательностью самого инстанцирования. Итд.
applegame, это не фантазии, это незнание теории.

Автор: korvin 13.07.15, 17:25
Цитата applegame @
Может тебе ссылки на его посты дать? Про детали реализации говорил я, а он как раз говорил, что истанцирование типов - это причина отрицать ПП в плюсах. Будешь мне тут рассказывать.

Буду тебе тут рассказывать:

Цитата D_KEY @
Шарп может генерировать, а может не генерировать - это деталь реализации. С++ не может не генерировать.


Добавлено
Цитата Qraizer @
По теме задачи. Математика умеет работать с бесконечностями. Ряды, последовательности, бесконечно точные вещественные, итп. Практические применения математических абстракций далеко не всегда могут быть реализованы в полной мере, и это не открытие Америки. Рекурсия должна быть остановлена, итерации ограничены итп.

Т.е. проблему останова уже решили? Не думаю, что выдёргивание шнура из розетки --- корректный способ останова рекурсии/цикла.

Автор: applegame 13.07.15, 17:28
Цитата korvin @
Буду тебе тут рассказывать:

Цитата D_KEY @
Шарп может генерировать, а может не генерировать - это деталь реализации. С++ не может не генерировать.

Ты почитай, что там вокруг говорилось. Тут он рассуждает, что это в Шарпе деталь реализации. А в плюсах - это не реализация, а "прописано в стандарте".

Автор: korvin 13.07.15, 17:29
Цитата Qraizer @
это не фантазии, это незнание теории.

Оу, ну расскажи нам теорию типов.

Автор: applegame 13.07.15, 17:29
Вот тебе еще ссылочки:
Цитата D_KEY @
Тут принципиальная разница как раз в том, что в C++(и в D?) у нас нет полиморфных типов/функций, у нас есть механизм генерации конкретных типов и функций в зависимости от параметров.
И это разница не в реализации.

И вот тут втирают про генерацию активно:
trait + impl vs class (сообщение #3611075)
А тут он признал что ПП таки в плюсах есть, но потом отмазался, что он имел что-то совсем другое:
Цитата D_KEY @
Параметрический полиморфизм есть в C++(признаю таки), но там нет полиморфных типов.


Добавлено
Цитата korvin @
Оу, ну расскажи нам теорию типов.
Ну что тебе еще рассказывать? В этих ваших институтах не учат что такое ПП? Ну тогда почитай ту самую лекцию из Гарварде, где и C++ и MLton упоминаются в одном контексте.

Автор: korvin 13.07.15, 17:31
Цитата applegame @
Ты почитай, что там вокруг говорилось. Тут он рассуждает, что это в Шарпе деталь реализации. А в плюсах - это не реализация, а "прописано в стандарте".

Ты сам внимательно почитай. Плюсы могли бы хоть только постоянно инстанциировать, сколько угодно. Проблема в возможности переопределения этого процесса. В шарпе, пусть он начнёт постоянно инстанциировать в compile-time, переопределить поведение будет всё равно невозможно.

Добавлено
Цитата applegame @
Ну что тебе еще рассказывать? В этих ваших институтах не учат что такое ПП? Ну тогда почитай ту самую лекцию из Гарварде, где и C++ и MLton упоминаются в одном контексте.

Пока что видно, что в ваших институтах его не учат, раз кое-кто не может даже пример написать и путает ПП с ad-hoc. =)

Автор: applegame 13.07.15, 17:35
Цитата korvin @
Ты сам внимательно почитай. Плюсы могли бы хоть только постоянно инстанциировать, сколько угодно. Проблема в возможности переопределения этого процесса. В шарпе, пусть он начнёт постоянно инстанциировать в compile-time, переопределить поведение будет всё равно невозможно.
То есть вырезав возможность перегружать функции и специализировать шаблоны мы "включим" ПП? :facepalm: :facepalm: :facepalm:
korvin и D_KEY придумали суперспособ добавлять новые фичи в язык путем вырезания функциональности :D

Автор: korvin 13.07.15, 17:37
Цитата applegame @
Ну тогда почитай ту самую лекцию из Гарварде, где и C++ и MLton упоминаются в одном контексте.

Кстати, по предоставленной мной ссылке нет ни C++, ни MLton.

Добавлено
Цитата applegame @
То есть вырезав возможность перегружать функции и специализировать шаблоны мы "включим" ПП? :facepalm: :facepalm: :facepalm:
korvin и D_KEY придумали суперспособ добавлять новые фичи в язык путем вырезания функциональности :D

То есть ты не видишь разницу между Сишными макросами и шаблонами? А что, первые же тоже умеют генерировать код под типы. Впрочем, это ожидаемо от D-шника. Был на другом форуме один товарищ, такие перлы выдвигал...

Автор: applegame 13.07.15, 17:41
Цитата Qraizer @
applegame, это не фантазии, это незнание теории.
Я даже не знаю что это такое. Хаскель головного мозга?

Автор: korvin 13.07.15, 17:42
Для особо одарённых: в ПП в принципе невозможно ничего перегрузить и переопределить.

Автор: applegame 13.07.15, 17:44
Цитата korvin @
То есть ты не видишь разницу между Сишными макросами и шаблонами? А что, первые же тоже умеют генерировать код под типы. Впрочем, это ожидаемо от D-шника. Был на другом форуме один товарищ, такие перлы выдвигал
То есть ты сам придумал про меня ерунду, сам ее утвердил и сам над ней поглумился. Ай, маладец. :D

Автор: korvin 13.07.15, 17:55
Цитата applegame @
То есть ты сам придумал про меня ерунду, сам ее утвердил и сам над ней поглумился. Ай, маладец. :D

Нет, я просто сделал вывод из твоих сообщений. Ты сам маладец. =)

Автор: applegame 13.07.15, 18:08
Ладно, я с вами не хочу ссориться из-за этого.
korvin, D_KEY, прошу прощения если где оскорбил. Останемся каждый при своем мнении.

Автор: D_KEY 13.07.15, 18:51
applegame, а я так и не понял, как можно называть средством ПП механизм, который допускает(а так же предполагает) специализацию. Из всех обсуждаемых языков, только C++ и D так делают

Добавлено
applegame, а если поставить вопрос иначе? Шаблоны являются средством параметрического или специального полиморфизма?

Автор: applegame 13.07.15, 18:53
Цитата D_KEY @
applegame, а я так и не понял, как можно называть средством ПП механизм, который допускает(а так же предполагает) специализацию. Из всех обсуждаемых языков, только C++ и D так делают
Ничем не могу помочь :)

Добавлено
Цитата D_KEY @
Шаблоны являются средством параметрического или специального полиморфизма?
Интересует мое мнение? И того и другого.

Автор: korvin 13.07.15, 18:58
Цитата applegame @
Ладно, я с вами не хочу ссориться из-за этого.
korvin, D_KEY, прошу прощения если где оскорбил. Останемся каждый при своем мнении.

Зачем ссорится? Мы просто ведём обсуждение, тут всегда каждый при своём мнении, в споре рождается истина и всё такое. Когда я в первый раз собрался встретиться с D_KEY'ем и Игорем в Питере, я подумал, что это плохая идея (в те времена мы с Игорем были на сильно противоположных сторонах и спорили жестко) я подумал, что встреча может закончиться мордобоем, но интернет --- это одно, а реальный мир --- другое. В общем я к тому, что никто с тобой ссориться не будет, а холиворы потому так и называются, тут по определению жесткий спор.

Автор: D_KEY 13.07.15, 19:01
Цитата TAPL
Параметрический полиморфизм (parametric polymorphism), позволяет придать участку кода «обобщенный» тип, используя переменные вместо настоящих типов, а затем конкретизировать, замещая эти переменные типами. Параметрические определения однородны: все экземпляры данного фрагмента кода ведут себя одинаково.


Цитата TAPL
Специализированный полиморфизм (ad-hoc polymorphism), напротив, позволяет полиморфному значению демонстрировать различное поведение при его «рассмотрении» относительно разных типов. Самый обычный пример специализированного полиморфизмаперегрузка (overloading), когда один и тот же символ функции соответствует различным реализациям; компилятор (или система времени выполнения, взависимостиоттого,идетлиречьостатическом(static)или динамическом (dynamic) разрешении перегрузки) выбирает подходящую реализацию для каждого случая применения функции, исходя из типов ее аргументов.


К какому из них относятся шаблоны?

Автор: applegame 13.07.15, 19:03
Цитата D_KEY @
К какому из них относятся шаблоны?
Я же уже писал. Шаблоны слишком общее понятие. Ими можно городить и PP и ad-hoc.

Автор: D_KEY 13.07.15, 19:03
Цитата applegame @
И того и другого.

Они могут быть в одном языке, но они не могут обеспечиваться одним механизмом, т.к. противоположны.

Автор: korvin 13.07.15, 19:07
Попробую объяснить Qraizer'у на его языке: ты много радел за инварианты в споре с делфишниками, так вот

length :: [a] -> Int

-- это инвариант, функция length всегда должна правильно подсчитывать длину списка. А код

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    template<>
    int length(vector<int> xs) { return 0; }


этот инвариант нарушает. ПП (в Хаскелле, Джаве и других ЯП) позволяют гарантировать этот инвариант (может я слово неправильно употребил, но, думаю, ты понял суть). В плюсах же шаблоны этой гарантии не дают.

Автор: applegame 13.07.15, 19:07
Цитата D_KEY @
Они могут быть в одном языке, но они не могут обеспечиваться одним механизмом, т.к. противоположны.
Я считаю так: если оно на уровне исходного кода одинаково для всех типов, то параметрическое, если разное, то специальное.

Автор: D_KEY 13.07.15, 19:10
Цитата applegame @
Ими можно городить и PP и ad-hoc.

А не получается ли у тебя, что чуть ли не любой ad-hoc может выразить ПП?

Автор: korvin 13.07.15, 19:12
Цитата applegame @
Я же уже писал. Шаблоны слишком общее понятие. Ими можно городить и PP и ad-hoc.

Пока нельзя в хэдере написать сигнатуру, а в cpp-файле --- реализацию, скомпилировать его отдельно, и юзать эту функцию, это не ПП, а Сишые макросы (с некоторыми бонуами), не более.

Автор: D_KEY 13.07.15, 19:13
Цитата applegame @
Цитата D_KEY @
Они могут быть в одном языке, но они не могут обеспечиваться одним механизмом, т.к. противоположны.
Я считаю так: если оно на уровне исходного кода одинаково для всех типов, то параметрическое, если разное, то специальное.

Но ведь в любой момент кто-то может специализировать тот или иной шаблон. Т.е. ты не можешь сказать, есть ли ПП в коде, пока весь его не рассмотришь и установить, есть ли специализация(или более сложные случаи разного поведения для разных типов)?

Автор: korvin 13.07.15, 19:14
Цитата applegame @
Я считаю так: если оно на уровне исходного кода одинаково для всех типов, то параметрическое, если разное, то специальное.

В этом и проблема: исходный код никому не интересен. Суть древнего процедурного программирования в использовании процедуры без анализа её исходного кода, а ты предлагаешь вернуться в каменный век.

Автор: D_KEY 13.07.15, 19:16
Цитата korvin @
это не ПП

Да, не ПП

Цитата
а Сишые макросы (с некоторыми бонуами), не более.

Нет :)

Автор: applegame 13.07.15, 19:18
Цитата korvin @
Попробую объяснить Qraizer'у на его языке: ты много радел за инварианты в споре с делфишниками, так вот

length :: [a] -> Int

-- это инвариант, функция length всегда должна правильно подсчитывать длину списка.
Не понял вброса. Никакой это не инвариант. Это библиотечная функция в Prelude. Вот полюбуйся:
http://ideone.com/UtXgp5

Добавлено
Цитата korvin @
Пока нельзя в хэдере написать сигнатуру, а в cpp-файле --- реализацию, скомпилировать его отдельно, и юзать эту функцию, это не ПП, а Сишые макросы (с некоторыми бонуами), не более.
То есть в ML нет ПП. Так и запишем.

Добавлено
Цитата korvin @
В этом и проблема: исходный код никому не интересен.
Ну тогда у нас везде ad-hoc по определению. x86 процессоры пока не научились одним и тем же кодом складывать float и int.

Автор: D_KEY 13.07.15, 19:23
Шаблоны представляют собой тесно интегрированный в язык механизм генерации кода, с возможностью задания дефолтной реализации и возможностью специализировать своё поведение для того или иного набора параметров(или части параметров и т.п.). Где тут параметрический полиморфизм? Ad-hoc. И только.

Автор: applegame 13.07.15, 19:25
Цитата D_KEY @
Шаблоны представляют собой тесно интегрированный в язык механизм генерации кода, с возможностью задания дефолтной реализации и возможностью специализировать своё поведение для того или иного набора параметров(или части параметров и т.п.)
Да.
Цитата D_KEY @
Где тут параметрический полиморфизм? Ad-hoc. И только.
PP by ad-hoc, да. Так тоже бывает. Например в ML.

Автор: korvin 13.07.15, 19:26
Цитата applegame @
Не понял вброса. Никакой это не инвариант. Это библиотечная функция в Prelude. Вот полюбуйся:
http://ideone.com/UtXgp5

То есть в ML нет ПП. Так и запишем.

Самый, что ни на есть, прямой. Результат функции length никак не зависит от типа элементов списка, в отличие от. Ты просто убрал стандартную функцию length. Твоя будет выдавать 0 для _любого_ списка, пусть в нм хоть Int будет, хоть Double, хоть String. Ты не можешь определить в одном модуле две length для разных типов в рамках ПП.

Лолшто? В ML сигнатуры более чем отделены от реализации. Я уж не говорю про параметризуемые модули(функторы).

Автор: D_KEY 13.07.15, 19:27
Цитата applegame @
Цитата D_KEY @
Где тут параметрический полиморфизм? Ad-hoc. И только.
PP by ad-hoc, да. Так тоже бывает. Например в ML.

В ml нельзя специализировать. Там ПП, потому что есть однородность(см. выше цитаты из TAPL).

Автор: skrambun 13.07.15, 19:28
Цитата D_KEY @
Предлагаю похоливарить на данную тему.

Коротко о trait'ах, impl'ах и данных на примере Rust.
Вместо "классического" ОО подхода, Rust предоставляет отдельные ортогональные механизмы.
Если классы у нас описывают и методы и данные, то в Rust мы задаем отдельно данные:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    struct Circle {
        x: f64,
        y: f64,
        radius: f64,
    }


и отдельно наборы методов:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    impl Circle {
        fn area(&self) -> f64 {
            std::f64::consts::PI * (self.radius * self.radius)
        }
    }

Можно задавать несколько блоков impl для типа.

Если классы предоставляют возможность наследования, то в Rust мы описываем интерфейсы посредством trait'а:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    trait HasArea {
        fn area(&self) -> f64;
    }


А реализацию trait'а для нашего типа описываем в соответствующем блоке impl.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    struct Circle {
        x: f64,
        y: f64,
        radius: f64,
    }
     
    impl HasArea for Circle {
        fn area(&self) -> f64 {
            std::f64::consts::PI * (self.radius * self.radius)
        }
    }
     
    struct Square {
        x: f64,
        y: f64,
        side: f64,
    }
     
    impl HasArea for Square {
        fn area(&self) -> f64 {
            self.side * self.side
        }
    }

Можно добавлять реализацию того или иного trait'а для того или иного типа, без внесения каких-либо изменений непосредственно в сам тип.

Наследование же поддерживается только на уровне наследование trait'ов:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    trait Foo {
        fn foo(&self);
    }
     
    trait FooBar : Foo {
        fn foobar(&self);
    }


Причем для реализующего trait типа это будет означать, что тип так же должен иметь impl и для "базового" trait'а:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    struct Baz;
     
    impl Foo for Baz {
        fn foo(&self) { println!("foo"); }
    }
     
    impl FooBar for Baz {
        fn foobar(&self) { println!("foobar"); }
    }


В trait'ах можно указывать реалиализацию методов по умолчанию:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    trait Foo {
        fn bar(&self);
     
        fn baz(&self) { println!("We called baz."); }
    }


Так же trait'ы используются для задания ограничений на generic-параметры, но это совсем другая история...

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

Небольшой пример с rustbyexample.com(предыдущие примеры были с doc.rust-lang.org):
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    trait Animal {
        // Static method signature; `Self` refers to the implementor type
        fn new(name: &'static str) -> Self;
     
        // Instance methods, only signatures
        fn name(&self) -> &'static str;
        fn noise(&self) -> &'static str;
     
        // A trait can provide default method definitions
        fn talk(&self) {
            // These definitions can access other methods declared in the same
            // trait
            println!("{} says {}", self.name(), self.noise());
        }
    }
     
    struct Dog { name: &'static str }
     
    impl Dog {
        fn wag_tail(&self) {
            println!("{} wags tail", self.name);
        }
    }
     
    // Implement the `Animal` trait for `Dog`
    impl Animal for Dog {
        // Replace `Self` with the implementor type: `Dog`
        fn new(name: &'static str) -> Dog {
            Dog { name: name }
        }
     
        fn name(&self) -> &'static str {
            self.name
        }
     
        fn noise(&self) -> &'static str {
            "woof!"
        }
     
        // Default trait methods can be overridden
        fn talk(&self) {
            // Traits methods can access the implementor methods
            self.wag_tail();
     
            println!("{} says {}", self.name, self.noise());
        }
    }
     
    struct Sheep { naked: bool, name: &'static str }
     
    impl Sheep {
        fn is_naked(&self) -> bool {
            self.naked
        }
     
        fn shear(&mut self) {
            if self.is_naked() {
                // Implementor methods can use the implementor's trait methods
                println!("{} is already naked!", self.name());
            } else {
                println!("{} gets a haircut", self.name);
     
                self.talk();
                self.naked = true;
            }
        }
    }
     
    impl Animal for Sheep {
        fn new(name: &'static str) -> Sheep {
            Sheep { name: name, naked: false }
        }
     
        fn name(&self) -> &'static str {
            self.name
        }
     
        fn noise(&self) -> &'static str {
            if self.is_naked() {
                "baaah"
            } else {
                "baaaaaaaaaaaah"
            }
        }
    }
     
    fn main() {
        // Type annotation is necessary in this case
        let mut dolly: Sheep = Animal::new("Dolly");
        let spike: Dog = Animal::new("Spike");
        // TODO ^ Try removing the type annotations
     
        dolly.shear();
     
        spike.talk();
        dolly.talk();
    }

Что это за говнокод? Это форум КОММЕРЧЕСКИХ ПРОГРАММИСТОВ и здесь можно размещать только КОММЕРЧЕСКИЙ КОД.

Автор: D_KEY 13.07.15, 19:28
Тебя не хватало тут :D

Автор: korvin 13.07.15, 19:30
Осталось определить ПП -- это коммерческая фича или промышленная =)))

Автор: applegame 13.07.15, 19:44
Цитата korvin @
Лолшто? В ML сигнатуры более чем отделены от реализации. Я уж не говорю про параметризуемые модули(функторы).
И их можно засунуть в бинарную либу и юзать без исходников? Оооооочень сильно сомневаюсь. Ибо как это сделать, если ML генерит для каждого типа свою реализацию?

Автор: korvin 13.07.15, 20:04
Цитата applegame @
И их можно засунуть в бинарную либу и юзать без исходников?

Да. И даже запихнуть в модули.

Цитата applegame @
Оооооочень сильно сомневаюсь.

Это бывает, если вместо чтения документации начинать фантазировать.

Цитата applegame @
Ибо как это сделать, если ML генерит для каждого типа свою реализацию?

RTFM!

Автор: Qraizer 13.07.15, 23:24
Цитата korvin @
Т.е. проблему останова уже решили? Не думаю, что выдёргивание шнура из розетки --- корректный способ останова рекурсии/цикла.
Не смешно, korvin. Классическая последовательность Фибоначчи: начальное условие рекурсии задаётся первыми двумя числами. Функция Аккермана: начальные условия задаются парой специализированных формул для выделенных значений аргументов функции. Метод математической индукции: начинается с рассмотрения специального вырожденного случая. Хватит примеров? Да, они АдХоковые, ну и что? Для них именно отдельное поведение требуется по определению. Но далее всё обобщённое. Почему надо удивляться, что моделирование бесконечной сущности тоже требует специального кода?

Добавлено
Цитата korvin @
Оу, ну расскажи нам теорию типов.
Не поймёте. Даже поняв, не примете, вы даже авторитетов принимать отказываетесь.

Добавлено
Цитата D_KEY @
Шаблоны являются средством параметрического или специального полиморфизма?
Неправильный вопрос. Не являются, а могут использоваться для. Параметрического. И специального, если оно потребуется, посредством специализаций. А ещё для чистого типизированного лямбда-исчисления посредством метакода, однако тут их возможности ограничены.

Добавлено
Цитата korvin @
Пока нельзя в хэдере написать сигнатуру, а в cpp-файле --- реализацию, скомпилировать его отдельно, и юзать эту функцию, это не ПП, а Сишые макросы (с некоторыми бонуами), не более.
Уже говорил, что можно. Почему так не делают, это отдельный вопрос. Самый простой ответ на него: это никому не удобно.

Добавлено
Цитата D_KEY @
Но ведь в любой момент кто-то может специализировать тот или иной шаблон.
Уже говорил, что это означает только лишь то, что при желании или по необходимости полиморфизм можно выключить. Это можно сделать для любого типа полиморфизма. Для динамического я тоже приводил примеры, однако применённый мною к такому ДП твой собственный аргумент касательно ПП ты почему-то опроверг, и я так и не понял, почему так нельзя для ПП, но можно для ДП. А ещё я могу отключить СП тривиальнейшим кастом типа функции. Что внезапно тоже является разновидностью СП. D_KEY, ты ещё не запутался в своих критериях применимости своих аргументов к своему виденью критериев наличия полиморфизма?

Автор: D_KEY 14.07.15, 05:25
Цитата Qraizer @
Цитата korvin @
Оу, ну расскажи нам теорию типов.
Не поймёте. Даже поняв, не примете, вы даже авторитетов принимать отказываетесь.

В качестве авторитетов ты предлагал пока только сам себя. Тебе же тут цитировали вики, работы, tapl и стандарт.

Цитата
Цитата D_KEY @
Шаблоны являются средством параметрического или специального полиморфизма?
Неправильный вопрос.

Правильный.

Цитата
Не являются, а могут использоваться для.

Имитации параметрического.

Цитата
Параметрического. И специального, если оно потребуется, посредством специализаций. А ещё для чистого типизированного лямбда-исчисления посредством метакода, однако тут их возможности ограничены.


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

Цитата
Цитата D_KEY @
Но ведь в любой момент кто-то может специализировать тот или иной шаблон.
Уже говорил, что это означает только лишь то, что при желании или по необходимости полиморфизм можно выключить.


А с чего ты взял, что он был включен? У тебя компилятор обязан создавать специализации(на уровне языка, а не генерации кода в реализации), а ты всегда имеешь возможность этими специализациями управлять. Где тут ПП?

Цитата
Это можно сделать для любого типа полиморфизма. Для динамического я тоже приводил примеры, однако применённый мною к такому ДП твой собственный аргумент касательно ПП ты почему-то опроверг, и я так и не понял, почему так нельзя для ПП, но можно для ДП.

Я объяснил. Ты не отключал специальный полиморфизм, а явно выбрал функцию и вызвал её. Ты сделал неполиморфный вызов. В случае же шаблонов у тебя всегда параметрическая полиморфность нарушена, т.к. нет гарантии однородности. Делая как бы полиморфный вызов ты не знаешь, будет ли там дефолтная реализация или какая-то переопределенная. В лучших традициях ad-hoc.

Цитата
А ещё я могу отключить СП тривиальнейшим кастом типа функции. Что внезапно тоже является разновидностью СП.

Ты ничего не можешь отключить. Ты просто вместо полиморфного вызова делаешь обычный.

Добавлено
Цитата Qraizer @
D_KEY, ты ещё не запутался в своих критериях применимости своих аргументов к своему виденью критериев наличия полиморфизма?

А ты все ещё думаешь, что обсуждается наличие полиморфизма, а не его вид?

Автор: applegame 14.07.15, 08:10
Цитата korvin @
Да. И даже запихнуть в модули.
Рассмотрим простую параметрически полиморфную функцию:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    foo a b = a + b

Ты полагаешь, что ее можно успешно засунуть в бинарную библиотеку оставив наружу только сигнатуру вкомпилировав в либу тело "a + b"? Как ты себе представляешь ABI, которое обеспечит вызов такой функции? Допустим ты сделаешь боксинг аргументов и передашь эти void* неизвестно с чем в либу. Полиморфная функция foo должна отправить эти void* в уже неполиморфную функцию "+", которой нужно знать как именно сложить эти аргументы. И тут получается облом - типы неизвестны.
Забавно, но Хаскель тебе не даст даже сигнатуру написать для такой функции, либо разрешит, но заставит указать принадлежность аргументов к тайпклассу, превратив таким образом функцию в параметрически неполиморфную.
ИМХО невозможно для параметрически полиморфной функции жестко отделить сигнатуру от реализации, ни в каком языке. Как компилятор на клиентской стороне будет делать тайп-чекинг если у него нет под рукой исходного кода этой функции? Мамой кланус эта функция умеет складывать целые числа, пиццу и кошек.
Цитата korvin @
RTFM!
То есть ты, надо думать, уже прочитал этот факинг мануал. Не мог бы ты тогда дать ссылочку на описание ABI обеспечивющей экспорт только сигнатур полиморфных функций без тела. А то я для MLton только смог найти ABI для обычных сишных функций и инфу о том, что, что MLton не поддерживает раздельную компиляцию совсем.
Цитата Qraizer @
Уже говорил, что можно.
Если шаблон на самом деле параметрически полиморфен, то полноценно экспортировать чисто сигнататуру без тела не получится. Только частично - конечное число реализаций. Если же в либе нет нужной реализации, то компилятор на клиентской стороне не сможет сгенерить ее без тела шаблона.

Автор: D_KEY 14.07.15, 08:14
Цитата applegame @
Рассмотрим простую параметрически полиморфную функцию:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    foo a b = a + b

Как ты собрался скаладывать значения в параметрически полиморфной функции?

Автор: applegame 14.07.15, 08:17
Цитата D_KEY @
Как ты собрался скаладывать значения в параметрически полиморфной функции?
Я не собираюсь их складывать, я собираюсь их передать неполиморфной функции сложения. Собственно так в полиморфных функциях и происходит.

Добавлено
Пардон, должно быть вот так:
Цитата applegame @
Я не собираюсь их складывать, я собираюсь их передать параметрически неполиморфной функции сложения. Собственно так в параметрически полиморфных функциях и происходит.

Автор: D_KEY 14.07.15, 08:41
Цитата applegame @
Я не собираюсь их складывать, я собираюсь их передать неполиморфной функции сложения.

Хорошо. Как ты собираешься в параметрически полиморфной функции передавать значения неизвестных типов в неполиморфную функцию?

Добавлено
Цитата applegame @
Собственно так в параметрически полиморфных функциях и происходит.

Это где такое?

Автор: applegame 14.07.15, 08:56
Цитата D_KEY @
Хорошо. Как ты собираешься в параметрически полиморфной функции передавать значения неизвестных типов в неполиморфную функцию?
Компилятор зная тип сам выполнит вызов нужной версии функции сложения или это будет сделано в run-time через нечто подобное таблице виртуальных функций.
Я вот не пойму, ты оспариваешь параметрическую полиморфность функции "foo a b = a + b"?
Цитата D_KEY @
Это где такое?
Везде в реальных программах. Сферический параметрический полиморфизм в вакууме не существует, в жестокой реальности любая ПП-функция рано или поздно упрется в ad-hoc полиморфизм.

Автор: D_KEY 14.07.15, 09:58
Цитата applegame @
Компилятор зная тип сам выполнит вызов нужной версии функции сложения

Какой нужной версии? Ты же говоришь о вызове неполиморфной функции.

Цитата
Я вот не пойму, ты оспариваешь параметрическую полиморфность функции "foo a b = a + b"?

Я оспариваю корректность вызова функции сложения в параметрически полиморфной функции.

Цитата
Сферический параметрический полиморфизм в вакууме не существует, в жестокой реальности любая ПП-функция рано или поздно упрется в ad-hoc полиморфизм.

Чем докажешь?

Автор: D_KEY 14.07.15, 10:16
applegame, любая генерация кода по некоторым параметрам является параметрическим полиморфизмом?

Автор: Qraizer 14.07.15, 12:51
Полный бред написал, D_KEY, извини. Один пример:
Цитата
Цитата
Цитата D_KEY @
Шаблоны являются средством параметрического или специального полиморфизма?
Неправильный вопрос.
Правильный.
Шаблоны; не являются; средством; какого-то там полиморфизма. Точно так же как классы не являются средством для ООП. И те, и другие – могут; для; этого использоваться. И далее в том же духе. Но уже этой цитаты достаточно, чтобы скомпрометировать любые твои предшествующие посты. Перед продолжением дискуссии настоятельно советую тебе освежить свои знания о теории, и особенно о Плюсах.

Добавлено
Цитата applegame @
Если шаблон на самом деле параметрически полиморфен, то полноценно экспортировать чисто сигнататуру без тела не получится. Только частично - конечное число реализаций. Если же в либе нет нужной реализации, то компилятор на клиентской стороне не сможет сгенерить ее без тела шаблона.
Может. Тела шаблона для этого не требуется, достаточно сгенерённого грамматического дерева, каковое полностью строится в точке определения, а не инстанцирования.

Автор: D_KEY 14.07.15, 12:54
Я тебе так же рекомендую освежить/приобрести теоретические знания. И не только о плюсах. За всю тему ты, к сожалению, не сказал ничего полезного.

Добавлено
Цитата Qraizer @
Полный бред написал, D_KEY, извини.

Где аргументация? Где ссылка на литературу(конкретная), где цитаты?

Цитата
Шаблоны; не являются; средством; какого-то там полиморфизма

Они являются механизмом генерации кода с поддержкой ad-hoc полиморфизма относительно параметров. Возможностей шаблонов достаточно для того, чтобы имитировать параметрический полиморфизм при определённых условиях(нарушаемых даже стандартной библиотекой с ее vector<bool>).

Автор: Qraizer 14.07.15, 13:02
Принято, D_KEY. Я удовлетворён. applegame, думаю, тоже.

P.S. Использование временных ограничений возможностей оппонента; сознательный игнор предложений почитать литературу; непризнание авторитетами людей с мировым именем; в конце концов нежелание поискать в Стандарте банальное "if implicit" и "if explicit" – поздравляю с принятием в ряды хейтерованных.

Автор: D_KEY 14.07.15, 13:06
Цитата Qraizer @
сознательный игнор предложений почитать литературу; непризнание авторитетами людей с мировым именем; в конце концов нежелание поискать в Стандарте банальное "if implicit" и "if explicit"

Хорошо себя описал. Ещё стоит добавить завышееное ЧСВ и будет ок.

Автор: DarkEld3r 14.07.15, 13:18
Цитата Qraizer @
"if implicit" и "if explicit"
Чисто из любопытства: что должно было найтись? А то поискал в драфте 14 стандарта и ничего.

Автор: D_KEY 14.07.15, 13:39
Цитата Qraizer @
Принято, D_KEY. Я удовлетворён. applegame, думаю, тоже.

applegame свою позицию старается аргументировать. Он пытался решить задачу и показывал код. Мы вместе разбирали код примеров и пр. А от тебя что было в этой теме?

Добавлено
Цитата Qraizer @
Использование временных ограничений возможностей оппонента

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

Автор: applegame 14.07.15, 14:25
Цитата D_KEY @
Какой нужной версии? Ты же говоришь о вызове неполиморфной функции.
Не понимаю тебя. Функция сложения параметрически неполиморфна, но она вполне ad-hoc полиморфна. Например в Haskell функция сложения является членом тайпкласса Num, для которого созданы инстансы для различных типов. Вот компилятор и выберет нужную версию или динамически диспетчеризует ее.
Цитата D_KEY @
Я оспариваю корректность вызова функции сложения в параметрически полиморфной функции.
То есть ты полагаешь, что функция "foo a b = a + b" параметрически не полиморфна, потому что в ней вызывается параметрически неполиморфная функция сложения?
Цитата D_KEY @
Чем докажешь?
Доказать не могу, возможно ошибаюсь. Но жду от тебя пример простенькой параметрически полиморфной функции, которая бы в итоге не вызывала параметрически неполиморфную функцию.
Цитата D_KEY @
applegame, любая генерация кода по некоторым параметрам является параметрическим полиморфизмом?
Не понимаю вопроса.
Цитата Qraizer @
Может. Тела шаблона для этого не требуется, достаточно сгенерённого грамматического дерева, каковое полностью строится в точке определения, а не инстанцирования.
Ну это по-сути и есть тело шаблона, просто упакованное в дерево. Если это дерево сериализовать и запихать в бинарную библиотеку, то наверное да, можно будет экспортировать только сигнатуру шаблона. Но это какой-то изврат однако :)

Автор: D_KEY 14.07.15, 14:37
Цитата applegame @
Функция сложения параметрически неполиморфна, но она вполне ad-hoc полиморфна

Ясно, просто ты написал "неполиморфна" и я тебя неверно понял.

Цитата
Цитата D_KEY @
Я оспариваю корректность вызова функции сложения в параметрически полиморфной функции.
То есть ты полагаешь, что функция "foo a b = a + b" параметрически не полиморфна, потому что в ней вызывается параметрически неполиморфная функция сложения?

В ней просто так нельзя вызвать функцию сложения. Для этого тебе нужно указать ограничение/тайпкласс/интерфейс.

Но смотрим https://wiki.haskell.org/Polymorphism#Param...ic_polymorphism
Цитата
Parametric polymorphism refers to when the type of a value contains one or more (unconstrained) type variables, so that the value may adopt any type that results from substituting those variables with concrete types.

И
Цитата
In Haskell, this means any type in which a type variable, denoted by a name in a type beginning with a lowercase letter, appears without constraints (i.e. does not appear to the left of a =>). In Java and some similar languages, generics (roughly speaking) fill this role.


Цитата
Доказать не могу, возможно ошибаюсь. Но жду от тебя пример простенькой параметрически полиморфной функции, которая бы в итоге не вызывала параметрически неполиморфную функцию.

id, map, etc. Много их.

Автор: MyNameIsIgor 14.07.15, 15:22
Полиморфную функцию сложения в хаскле можно написать так.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    add :: a -> a -> (a -> a -> a) -> a
    add l r plus = plus l r

И мне не очень понятно, почему, если я сделаю так
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    add :: Num a => a -> a -> a
    add l r = l + r

то параметрический полиморфизм вдруг исчезает? Почему если я передаю функцию, это полиморфизм, если за меня делает компилятор, то не полиморфизм :-?

Автор: applegame 14.07.15, 15:30
Цитата D_KEY @
В ней просто так нельзя вызвать функцию сложения. Для этого тебе нужно указать ограничение/тайпкласс/интерфейс.
Как это нельзя? Очень даже можноЭто уже обсуждалось
Цитата D_KEY @
id, map, etc. Много их.
id пожалуй да, ничего не делает, просто возвращает то что получила на вход. Как и множество функций возвращающие одно и тоже значение вне зависимости от аргумента. Но возьмем map, эта функция уже работает с элементами списка, и если например она к ним прибавляет 1, то вот тебе и ad-hoc полиморфизм: http://ideone.com/rno7V2

Добавлено
Цитата MyNameIsIgor @
Полиморфную функцию сложения в хаскле можно написать так.
Это не функция сложения.

Автор: D_KEY 14.07.15, 16:08
Цитата applegame @
Цитата D_KEY @
В ней просто так нельзя вызвать функцию сложения. Для этого тебе нужно указать ограничение/тайпкласс/интерфейс.
Как это нельзя? Очень даже можно

Выпиши тип функции и прочти определение, что я цитировал.

Цитата
id пожалуй да, ничего не делает, просто возвращает то что получила на вход. Как и множество функций возвращающие одно и тоже значение вне зависимости от аргумента. Но возьмем map, эта функция уже работает с элементами списка, и если например она к ним прибавляет 1, то вот тебе и ad-hoc полиморфизм: http://ideone.com/rno7V2

Нет тут ad-hoc полиморфизма. Ты вызвал параметрически полиморфную функцию map над списком чисел и передал ей функцию, работающую с типами, соответствующими Num. ad-hoc тут уже в самой функции, а не в map.

Автор: MyNameIsIgor 14.07.15, 17:45
Цитата applegame @
Цитата MyNameIsIgor @
Полиморфную функцию сложения в хаскле можно написать так.
Это не функция сложения.

Это в том числе и функция сложения.
И таки я не понял: а каким образом эта фраза отвечает на мой вопрос?

Автор: applegame 14.07.15, 18:23
Цитата MyNameIsIgor @
И таки я не понял: а каким образом эта фраза отвечает на мой вопрос?
Эта фраза не отвечает на вопрос, потому что эта фраза отвечала на твою фразу, в которой не было никакого вопроса.
Цитата D_KEY @
Выпиши тип функции и прочти определение, что я цитировал.
Ты мои посты вообще читаешь? Это определение я прочитал раньше тебя.
По по поводу типов ты оказался прав. Я сильно удивился, но у функции "foo a b = a + b" действительно тип "foo :: Num a => a -> a -> a", то бишь она параметрически неполиморфна.
Но это ладно. Потом я попытался создать полиморфную функцию сложения аналогично предложенному MyNameIsIgor добавив каррирование функции +. Результат меня еще больше удивил:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Prelude> let { foo :: (a -> a -> a) -> a -> a -> a; foo f a b = f a b }
    Prelude> :t foo
    foo :: (a -> a -> a) -> a -> a -> a
    Prelude> foo (+) 0.5 0.4
    0.9
    Prelude> let bar = foo (+)
    Prelude> :t bar
    bar :: Integer -> Integer -> Integer <<<<<<<<<<<<<<<<<<<<<<<<< WTF?!
    Prelude> bar 0.5 0.4
     
    <interactive>:53:5:
        No instance for (Fractional Integer) arising from the literal `0.5'
        Possible fix: add an instance declaration for (Fractional Integer)
        In the first argument of `bar', namely `0.5'
        In the expression: bar 0.5 0.4
        In an equation for `it': it = bar 0.5 0.4

Автор: MyNameIsIgor 14.07.15, 18:37
Цитата applegame @
Я сильно удивился, но у функции "foo a b = a + b" действительно тип "foo :: Num a => a -> a -> a", то бишь она параметрически неполиморфна.

Что-то все эту фразу из какой-то странной вики по хаскелю приняли на веру, но пока нет никаких пруфов - это тупой вброс.
Цитата applegame @
Потом я попытался создать полиморфную функцию сложения аналогично предложенному MyNameIsIgor добавив каррирование функции +

На самом деле вы частично применили функцию foo, передав ей функцию +
Цитата applegame @
Результат меня еще больше удивил

Почему-то был взят первый тип, удовлетворяющий Num. Если явно задать bar :: Num a => a -> a -> a, то всё нормально.

Автор: applegame 14.07.15, 18:56
Цитата MyNameIsIgor @
Что-то все эту фразу из какой-то странной вики по хаскелю приняли на веру, но пока нет никаких пруфов - это тупой вброс.
Может и так, придет korvin и D_KEY и аргументируют. Это же каноничъно-православное определение ПП. А не каноничЪное ПП и в плюсах есть.
Цитата MyNameIsIgor @
На самом деле вы частично применили функцию foo, передав ей функцию +
Вроде это и называется каррирование. Нет?
Цитата applegame @
добавив каррирование функции +

Цитата MyNameIsIgor @
Почему-то был взят первый тип, удовлетворяющий Num.
Это и странно.
Вот так тоже весело

Автор: Qraizer 14.07.15, 18:58
Цитата D_KEY @
applegame свою позицию старается аргументировать. Он пытался решить задачу и показывал код. Мы вместе разбирали код примеров и пр. А от тебя что было в этой теме?
Я тоже старался. Увы, вопросы касательно разницы в твоих выводах о ПП и ДП в ровно одинаковых условиях ты проигнорировал. Об СП у тебя вообще поверхностные представления, как я погляжу. Вообще, не надо мешать в один клубок два разных вопроса: быть или не быть ПП в Плюсах, и как решить вон ту задачу. applegame пытался решить и решил её, вы её зафейлили посредством изменений её условий.
От меня в этой теме было достаточно. D_KEY, если считаешь, что в теме, напиши условие задачи на математическом языке, и внимательно на него посмотри. Потом перечитай тему. Желательно целиком.

Добавлено
Цитата applegame @
Это же каноничъно-православное определение ПП. А не каноничЪное ПП и в плюсах есть.
Канонично правильное – только в теории. На практике обязательно должны будут присутствовать те или иные ограничения, неважно, как конкретно и где конкретно реализованные. Хоть в метакоде программера, хоть в компиляторе, хоть в языке. Знаешь ли, double тоже плохо подходит для реализации арифметического Хаффмана, хотя в теории всё выглядит очень красиво.

Автор: applegame 14.07.15, 19:34
Цитата Qraizer @
Канонично правильное – только в теории...
Это был сарказм. Ведь наши оппоненты как раз таки взяли некое определение ПП и считают его истной. Так сказать Канонично-Православный Полный Полиморфизм - КППП.

Автор: D_KEY 14.07.15, 20:40
Цитата Qraizer @
Увы, вопросы касательно разницы в твоих выводах о ПП и ДП в ровно одинаковых условиях ты проигнорировал.

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

Добавлено
Цитата Qraizer @
applegame пытался решить и решил её, вы её зафейлили посредством изменений её условийй.

Какие условия были изменены?йй

Добавлено
Цитата applegame @
Я сильно удивился, но у функции "foo a b = a + b" действительно тип "foo :: Num a => a -> a -> a"

Что тут удивительного? Как иначе ты предлагаешь типизировать? Как иначе разрешить вызов + ?

Добавлено
Цитата applegame @
Может и так, придет korvin и D_KEY и аргументируют.

Мы сейчас конкретно за хаскель говорили. Возможно, определение и неверно. Но оно не противоречит ранее сказанному, скорее ужесточает.

Цитата
А не каноничЪное ПП и в плюсах есть.

Есть ли? Вот я пытаюсь примерить твою точку зрения к плюсам, но выходит плохо, даже вот не знаю, является ли std::vector ПП типом? И тут дло даже не в bool, который ломает ПП явно. Что мы делаем с аллокаторами, компараторами, хеш-функциями, трейтами в параметрах стандартных контейнеров? Неужели это тоже ПП?

Добавлено
Цитата Qraizer @
Об СП у тебя вообще поверхностные представления, как я погляжу

Возможно. Так укажи на ошибки и аргументируй свою позицию. Дай ссылки и цитаты.

Автор: applegame 15.07.15, 03:50
Цитата D_KEY @
Как иначе ты предлагаешь типизировать? Как иначе разрешить вызов + ?
А в чем проблема? Даже мой сраный D может все типизировать в этом случае, а крутой Хаскель не может? Типизируется же функция map если ей подсунуть (+).

Добавлено
Цитата D_KEY @
Есть ли? Вот я пытаюсь примерить твою точку зрения к плюсам, но выходит плохо, даже вот не знаю, является ли std::vector ПП типом? И тут дло даже не в bool, который ломает ПП явно. Что мы делаем с аллокаторами, компараторами, хеш-функциями, трейтами в параметрах стандартных контейнеров? Неужели это тоже ПП?
В твоем "хаскельном" понимании - нет. В общепринятом - да.

Автор: D_KEY 15.07.15, 06:21
Цитата applegame @
А в чем проблема? Даже мой сраный D может все типизировать в этом случае

Не даже, а только он и c++ и могут. Потому, что шаблоны.

Цитата
Типизируется же функция map если ей подсунуть (+).

Ну так ты же передаёшь параметр.

Цитата
В общепринятом - да.

Что это ещё за общепринятое понимание? Я вот tapl цитировал, это общепринятое понимание?

И ты же сам говорил, что если если есть специализация, то значит нет ПП. Или я тебя не так понял?

Автор: D_KEY 15.07.15, 08:05
Вот интересно, все ли тут согласны с

Цитата wiki
Some implementations of type polymorphism are superficially similar to parametric polymorphism while also introducing ad hoc aspects. One example is C++ template specialization.


И не является ли это консенсусом или хотя бы компромиссом в нашем холиваре?

Автор: Qraizer 15.07.15, 09:52
Цитата D_KEY @
Так укажи на ошибки и аргументируй свою позицию.
Касты являются проявлением СП, только диспетчером тут выступает сам программист. Ссылку на вики найти нетрудно.
Цитата D_KEY @
И не является ли это консенсусом или хотя бы компромиссом в нашем холиваре?

Этот консенсус появился в теме давным давно, лень искать, но вроде бы за авторством MyNameIsIgor-я. Ты его разве не видел? Мы с applegame всю тему пытаемся показать, что реализация не имеет значения, главное получаемый эффект. И если рассматривать жизненный цикл шаблонов от определения до точки использования, ПП проявляется в полной мере. Если рассматривать цикл шире, за точку использования, то applegame всю тему пытается показать абсурдность этой позиции, т.к. на уровне исполняемого кода всё равно всегда и во всех языках будет АдХок. Я бы ещё добавил в исключения аппаратные реализации собственно языка (а также их эмуляторы в ряде случаев), но это уже технические детали. Я тоже апеллировал к ДП, который полиморфизм подтипов, ведь реализация даже в коде разбросана по разным методам (точнее, по перекрытым методам разных классов), а значит тоже АдХок (диспетчеризируемый в run-time обобщённым нетипизированным ПП-кодом, сгенерированным компилятором), и вот на этот счёт твои возражения мне вообще непонятны.
Ещё добавлю, что ПП не обязан обеспечивать работу кода для любого типа вообще, он может ограничить множество типов конечным набором, и от этого ПП быть не перестаёт. Впрочем, я об этом писал не один раз: если математически такие случаи описываются интуитивно, то язык программирования может предлагать для обеспечения подобных ограничений этого менее наглядные средства, включая метакод, специализации или run-time логику уровня ВМ.

Добавлено
Цитата D_KEY @
Вот я пытаюсь примерить твою точку зрения к плюсам, но выходит плохо, даже вот не знаю, является ли std::vector ПП типом?
В общем случае нет, но таковыми являются его аргументы. В частных случаях, например, для std::stack<>, является.

Автор: applegame 15.07.15, 09:55
Цитата D_KEY @
Что это ещё за общепринятое понимание? Я вот tapl цитировал, это общепринятое понимание?
Да, в TAPL общепринятое понимание.
Но я не вижу в чем определение данное в TAPL противоречит моей точке зрения.
Ты приравниваешь два разных понятия: "функция f специализировна" и "функция f может быть специализирована".
Из TAPL ясно следует, что если функция специализирована (на уровне определения, а не на уровне бинарного кода), то она точно параметрически не полиморфна. Но я пока не нашел там упоминаний о том, что сама возможность специализировать уже декларированную параметрически полиморфную функцию - делает ее параметрически неполиморфной. Вот именно с этого момента заканчивается общепринятое понимание и начинается твое частное.
Цитата D_KEY @
И не является ли это консенсусом или хотя бы компромиссом в нашем холиваре?

Эта цитата уже была приведена тобой, что-то не помогло :D

Добавлено
Цитата Qraizer @
Ещё добавлю, что ПП не обязан обеспечивать работу кода для любого типа вообще, он может ограничить множество типов конечным набором, и от этого ПП быть не перестаёт.
Чисто в хаскельном понимании у настоящей ПП-функции не должно быть ограничений типов в сигнатуре. И кстати опять повторюсь: в решении той задачи на Хаскеле нет ПП в хаскельном же понимании. Параметры фйнкции main' ограничены тайпклассом ScalarProduct. Получается, что эта задача имеет прямое отношение к полиморфной рекурсии, но не имеет никакого отношения к параметрическому полиморфизму. По крайней мере в хаскельном понимании.
Лично я склонен считать (как и Qraizer и MyNameIsIgor), что функция с сигнатурой "Num a => a -> a -> a" вполне параметрически полиморфна.

Автор: D_KEY 15.07.15, 10:56
Цитата Qraizer @
Цитата D_KEY @
Так укажи на ошибки и аргументируй свою позицию.
Касты являются проявлением СП, только диспетчером тут выступает сам программист. Ссылку на вики найти нетрудно.

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

Принято.

Цитата
Цитата D_KEY @
И не является ли это консенсусом или хотя бы компромиссом в нашем холиваре?

Этот консенсус появился в теме давным давно, лень искать, но вроде бы за авторством MyNameIsIgor-я.

Это я цитировал. Но тогда не комментировал.

Цитата
Мы с applegame всю тему пытаемся показать, что реализация не имеет значения, главное получаемый эффект.

Так эффект разный. Реализация не имеет значения для некоторых других языков, но не для C++, который обязуется делать специализации.

Цитата
И если рассматривать жизненный цикл шаблонов от определения до точки использования, ПП проявляется в полной мере.

Может стоит рассмотреть пример?

Цитата
Я тоже апеллировал к ДП, который полиморфизм подтипов, ведь реализация даже в коде разбросана по разным методам (точнее, по перекрытым методам разных классов), а значит тоже АдХок (диспетчеризируемый в run-time обобщённым нетипизированным ПП-кодом, сгенерированным компилятором), и вот на этот счёт твои возражения мне вообще непонятны.

Да, это ad-hoc. Какие возражения?
И да, в плюсах нет полиморфизма подтипов, если я правильно понимаю его.

Цитата
Ещё добавлю, что ПП не обязан обеспечивать работу кода для любого типа вообще, он может ограничить множество типов конечным набором, и от этого ПП быть не перестаёт.

Это вопрос спорный. В Haskell, например, не так. И определение из tapl как раз ближе к такой трактовке.

Автор: D_KEY 15.07.15, 10:58
Цитата applegame @
Да, в TAPL общепринятое понимание.
Но я не вижу в чем определение данное в TAPL противоречит моей точке зрения.

Вот в этом:
Цитата
Параметрические определения однородны: все экземпляры данного фрагмента кода ведут себя одинаково.


Добавлено
В стандарте C++ нет ни слова о параметрическом полиморфизме(или я ошибаюсь?). С тем, что имитация ПП есть в C++ вроде все согласны. Что вам еще нужно?

Добавлено
Цитата applegame @
Ты приравниваешь два разных понятия: "функция f специализировна" и "функция f может быть специализирована".

Во-первых, в стандарте C++ говорится, что специализация будет создана компилятором, если ее не создаст программист. Цитаты в теме есть.
Во-вторых, еще раз говорю, из твоей позиции вытекает, что ты не в состоянии сказать, полиморфна ли функция, пока не перелопатишь весь код программы и не убедишься в отсутствии/наличии специализации? Ты уверен, что тут все в порядке?

Цитата
Но я пока не нашел там упоминаний о том, что сама возможность специализировать уже декларированную параметрически полиморфную функцию - делает ее параметрически неполиморфной.

Сама возможность специализировать виртуальную функцию делает виртуальные функции механизмом ad-hoc полиморфизма. Тут аналогично.

Цитата
Лично я склонен считать (как и Qraizer и MyNameIsIgor), что функция с сигнатурой "Num a => a -> a -> a" вполне параметрически полиморфна.

Это вопрос спорный... Функция определена не на всех типах, а значит об однородности уже речи не идет. Но, допустим, мы будем считать ее параметрически полиморфной функцией, которая в своем коде уже вызывает ad-hoc полиморфный +.

Автор: Qraizer 15.07.15, 12:37
Вот. Теперь по крайней мере нет противоречий в твоих постах.

Автор: korvin 15.07.15, 12:54
Цитата applegame @
Ты полагаешь, что ее можно успешно засунуть в бинарную библиотеку оставив наружу только сигнатуру вкомпилировав в либу тело "a + b"? Как ты себе представляешь ABI, которое обеспечит вызов такой функции? Допустим ты сделаешь боксинг аргументов и передашь эти void* неизвестно с чем в либу. Полиморфная функция foo должна отправить эти void* в уже неполиморфную функцию "+", которой нужно знать как именно сложить эти аргументы. И тут получается облом - типы неизвестны.
Забавно, но Хаскель тебе не даст даже сигнатуру написать для такой функции, либо разрешит, но заставит указать принадлежность аргументов к тайпклассу, превратив таким образом функцию в параметрически неполиморфную.
ИМХО невозможно для параметрически полиморфной функции жестко отделить сигнатуру от реализации, ни в каком языке. Как компилятор на клиентской стороне будет делать тайп-чекинг если у него нет под рукой исходного кода этой функции? Мамой кланус эта функция умеет складывать целые числа, пиццу и кошек.

Не просто могу, а все функции в Хаскелле (в т.ч. и параметрически полиморфные) запиханы в библиотеки с торчащими наружу только сигнатурами. Как и в ML. Как и классы в Java и C#.

Хаскелл же не даст тебе написать такую функцию без указания тайпкласса, потому что (+) является методом тайпкласса. С тем же успехом ты мог бы жаловаться, что D/C++/whatever-static не даст тебе написать функцию

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Object add(Object x, Object y) { return x + y; }


а потребует, например,

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Number add(Number x, Number y) { return x + y; }

Автор: DarkEld3r 12.08.15, 14:23
"Кстати", в расте специализацию для дженериков хотят, наконец-то, добавить.

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)