статически типизированный язык с чертами и перегрузкой функций?

Существует ли любой язык, который:

  • статически (и строго) типизирован
  • поддерживает дженерики (шаблоны)
  • поддерживает трейты/множественное наследование/делегирование интерфейса
  • разрешает перегрузку функций (также унаследованных членов)

В принципе, в псевдокоде я хочу этого:

class Template<SomeType> { void function(SomeType i) {} }
class Composed extends Template<int>, Template<double> { };
Composed composed;
composed.function(3); //calls function(int)
composed.function(2.5); //calls function(double)

Из вики-списка http://en.wikipedia.org/wiki/Multiple_inheritance#Implementations Я пытался писать код на следующих языках программирования (я также включил новейшие еще неопубликованные языки версии 1.0 (2013), такие как Kotlin, Ceylon):

  • C++ почти возможно, но не может разрешить перегруженную функцию для составной переменной
  • Ошибка компиляции Scala: трейт унаследован дважды; даже если его обмануть косвенным наследованием, он все равно выдает ошибку; см. также вопрос другого парня о множественном наследовании общих миксинов в Scala
  • Ошибка компиляции Eiffel: нет перегрузки функции
  • Черты Ceylon не могут иметь переменных (и защищенных членов, поэтому никаких трюков для хранения данных в производном классе, черты в Ceylon бесполезны)
  • Фантом без общих шаблонов/шаблонов
  • Ошибка компиляции Kotlin: супертип появляется дважды; делегирование выглядит уникальным, но оно бесполезно, потому что нельзя получить доступ ни к защищенным членам, ни к переменной, которая делегирована
  • Rust не перегружает функции; трейты не могут хранить данные; трейты не могут иметь определенных методов (с телом) - проблема, над которой работают;
  • Ошибка компиляции OCaml: нет перегрузки функций; он также не проверял тип параметра для моей функции, так что насколько он «статически типизирован»?!
  • Ошибка компиляции Curl: нет перегрузки функции; он также не проверяет тело функции, если оно не вызывается, так насколько же оно на самом деле «статически типизировано»? он вообще компилируется или интерпретируется?!
  • Gosu — плохая шутка, его вообще нельзя использовать: нельзя написать и реализовать простой интерфейс с одним матодом (ошибка: ClassFormatError: Illegal field modifiers). Кто на самом деле использует этот язык?!

Кстати: я подумал об этой проблеме, когда пытался вынести поддержку Java для слушателей в отдельный класс (во многих классах есть: List‹ListenerType› ... addListener(...) ... removeListener(...) )

С++ почти работает:

template <typename T>
class Template { public: void function(T i) {} };
class Composed : public Template<int>, public Template<double> { };

Composed composed;
composed.Template<int>::function(3); //i want: composed.function(3);
((Template<double>&)composed).function(2.5); //i want: composed.function(2.5);

Изменить: в С++ проблема заключается в сокрытии функции наследования. См. также Функция с тем же именем, но с другой подписью в производном классе. и Почему переопределенная функция в производном классе скрывает другие перегрузки базового класса?

Редактировать 2: в С++ с шаблонами и частичной специализацией существует возможность подвоха, позволяющего упростить использование черты:

#include <iostream>
#include <typeinfo>

class Void { };
template <class A, class B> class CleverTrait;
template <class A, class B> class CleverTrait;
template <class A> class CleverTrait<A, Void>
{
public:
    void function(A arg) { std::cout << "Hello for type " << typeid(A).name() << std::endl; }
};
template <class A, class B> class CleverTrait<A, CleverTrait<B, Void> > : public CleverTrait<B, Void>
{
public:
    using CleverTrait<B, Void>::function;
    void function(A arg) { std::cout << "Hello for type " << typeid(A).name() << std::endl; }
};

class ComposedByClever : public CleverTrait<double, CleverTrait<int, Void> > { };

int main()
{
    ComposedByClever composedByClever;
    composedByClever.function(5);
    composedByClever.function(2.3);
    return 0;
}

Это означает, что данный конкретный пример в C++ работает, однако, если несвязанные трейты имеют общее имя функции, в C++ невозможно наследовать и использовать все функции.

Изменить 3: я также должен проверить любой язык программирования, поддерживающий миксины: http://en.wikipedia.org/wiki/Mixins#Programming_languages_that_use_mixins

  • D работает, но только через миксин для манипуляций со строками, поэтому рефакторинг в таком случае нарушается: mixin(GenerateSomething!("IfClassNameHereManualRenaming"));

Редактировать 4: добавлен языковой комментарий «Госу».

Редактировать 5: Язык программирования Gosu имеет обновление 0.10.2, в котором исправлена ​​проблема с неработающими интерфейсами. Однако, несмотря на то, что они утверждают, что у них есть овеществленные дженерики и делегирование, делегирование + овеществленные дженерики не работает.


person peenut    schedule 26.07.2013    source источник
comment
Помещение public: using Template<int>::function; using Template<double>::function; внутри класса Composed заставляет пример C++ работать без квалификации Template<...>:: или приведения (например, что вы хотите).   -  person dyp    schedule 26.07.2013
comment
Вы также можете использовать это в C++: public: template<typename T> void function(T i) { static_cast<Template<T>*>(this)->function(i); } (или просто Template<T>::function(i) вместо приведения, хотя это может скрыть проблему со статическими функциями-членами: когда они не унаследованы, это все равно будет работать).   -  person dyp    schedule 26.07.2013
comment
Еще одна странная идея: в C++ вы могли бы превратить множественное наследование в одиночное наследование, используя вариант CRTP, например. class Composed : public Template<int, Template<double>>, где Template<int,..> происходит от Template<double> и использует директиву using для перегрузки function. С помощью этого трюка вам не нужно было бы вручную указывать using для каждого базового класса.   -  person dyp    schedule 26.07.2013
comment
@DyP: (используя) Я не знал об использовании, спасибо. Но мне все равно пришлось бы перечислять все функции, что противоречит цели черты. (статическое приведение) похоже на использование, но становится более умным, если наследуется более чем в 2 раза.   -  person peenut    schedule 26.07.2013
comment
@DyP: (шаблоны) работает, можно и без вариативных шаблонов, может и с ними тоже, я ими еще не пользовался.   -  person peenut    schedule 26.07.2013
comment
Педантичность: проблема в C++ не в сокрытии имени, а в двусмысленности при поиске имени. Сокрытие имени может произойти, например. когда производный класс объявляет член с тем же именем, что и базовый класс. Здесь у нас есть члены только в базовых классах. Неоднозначность описана в [class.member.lookup]/6.   -  person dyp    schedule 26.07.2013


Ответы (1)


C++: странная идея, похожая на CRTP (см. комментарий к OP) я должен был быть больше похож на:

#include <typeinfo>
#include <iostream>

struct Dummy
{
private:
  // dummy type to prevent overload resolution from ever choosing this overload
    struct ParamDummy { explicit ParamDummy(); };
public:
    void function(ParamDummy);
};

template<typename T, typename Base = Dummy>
struct Template
    : Base
{
    using Base::function;
    void function(T i) { std::cout << typeid(T).name() << std::endl; }
};

struct SecondBaseClass
{
    void function2() { std::cout << "function2" << std::endl; }
};

struct Composed
    : Template<int, Template<double>>, SecondBaseClass
{};

int main()
{
    Composed c;
    c.function(5.0);
    c.function(5);
    c.function2();
}

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

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

Ситуация усложняется, если базовые классы совместно используют только некоторые, но не все функции-члены.

person dyp    schedule 26.07.2013
comment
простая перегрузка функций - но только из того же трейта, иначе вы не знаете, что вам следует использовать - person peenut; 26.07.2013
comment
@peenut Вы используете директиву using для всех имен функций вашего собственного класса, вам не нужно знать имена функций ваших базовых классов. Возможно, вы могли бы добавить какой-нибудь трюк, чтобы заставить его работать, если в вашем базовом классе нет члена с таким же именем, как у вас. - person dyp; 26.07.2013
comment
@peenut На самом деле, вы можете использовать этот трюк, если хотите перегрузить функции (сложенное одиночное наследование) и множественное наследование для несвязанных базовых классов. - person dyp; 26.07.2013