Trait a konstruktor

BoneFlute

  • *****
  • 1 981
    • Zobrazit profil
Trait a konstruktor
« kdy: 18. 12. 2020, 00:25:06 »
Zdravím. Měl bych tu zase něco z akademického programování.

Předpokládejme nějaký supr jazyk. A chceme tam mít podporu traitů - aby nedocházelo ke zneužívání dědičnosti. Takže například:

Kód: [Vybrat]
trait A {
    private int id;
    public int getId() { return this.id; }
}
trait B {
    private string name;
    public string getName() { return this.name; }
}

class Foo {
    use A;
    use B;
}
Otázka zní, jak hodnoty z těch traitů inicializovat? A navíc můžeme předpokládat, že třída komponující traity by neměla mít právo přistupovat k privátním členům.

Určitě nechceme settery, potřebujeme konstruktor, abychom měli kontrolu nad stavem objektu. Jak tento problém řešit?

Děkuji za příspěvky, ať už z reálných jazyků, nebo klidně pouhá úvaha.


BoneFlute

  • *****
  • 1 981
    • Zobrazit profil
Re:Trait a konstruktor
« Odpověď #1 kdy: 18. 12. 2020, 00:32:37 »
Mě napadají dvě řešení:

Kód: [Vybrat]
class Foo {
    use A;
    use B;
    public constructor(int id, string name) {
        A.constructor(id);
        B.constructor(name);
    }
}

nebo odvážnější:

Kód: [Vybrat]
trait A {
    readonly int id;
}
trait B {
    readonly string name;
}

class Foo {
    use A;
    use B;
    readonly Address address;
    // konstruktor se vygeneruje automaticky, na základě příznaku "readonly", a případně se může přetížit.
    public constructor(string name, Address address) {
        this.constructor(null, name, address);
    }

}

Ink

  • *****
  • 655
    • Zobrazit profil
    • E-mail
Re:Trait a konstruktor
« Odpověď #2 kdy: 18. 12. 2020, 09:59:42 »
V Rustu, pokud vím, trait nemá fieldy. Proč je tam potřebuješ mít, patří tam vůbec?

Re:Trait a konstruktor
« Odpověď #3 kdy: 18. 12. 2020, 10:07:56 »
V Rustu, pokud vím, trait nemá fieldy. Proč je tam potřebuješ mít, patří tam vůbec?
Ve Scale ano.

Re:Trait a konstruktor
« Odpověď #4 kdy: 18. 12. 2020, 10:36:14 »
Jak psali přede mnou, Rustovské traity fieldy nemají, a nemám pocit, že by mi tam chyběly.

Scala je má, inicializují se přímo v definici traitu

trait T {
  val f: Integer = 1
}

class C extends T {
  def printF(): Unit = {
     println(f)
  }
}

Kdybych chtěl inicializaci odložit, tak z toho fieldu udělám abstraktní metodu, používá se to stejně jako field.

trait T {
  def f: Integer
}

class C(val f: Integer) extends T {
  def printF(): Unit = {
    println(f)
  }
}


Re:Trait a konstruktor
« Odpověď #5 kdy: 18. 12. 2020, 14:03:34 »
Podle me fieldy do traitu nepatri.
Pokud chci jen rict "tahle vec umi vratit id" a "tahle vec umi vratit jmeno" tak na to jsou interfacy/protokoly.
A ten priklad s getterama mi dost smrdi zneuzivanim traitu (k dedicnosti?).
Podle me trait ma prinest chovani a ne nejak obalovat hodnoty.
Skoro se desim predstavy ze by to dokonce mohli byt nejaky mutable fieldy.

No ale jestli to musi bejt a jestli to teda muzou bejt konstanty... tak bych uprednostnil neco takoveho:

Kód: [Vybrat]
class Foo {
        use A with id;
        use B with name;
        Address address;
}

'readonly' neni treba, protoze vsechno je readonly.... a z toho 'with id' by si uz kompiler mohl odvodit jak ma vypadat konstruktor.

Idris

  • *****
  • 2 286
    • Zobrazit profil
    • E-mail
Re:Trait a konstruktor
« Odpověď #6 kdy: 18. 12. 2020, 14:26:42 »
Obecně se domnívám, že data do traitů nepatří, ale kdyby nebylo zbytí, tak by se mi líbilo něco jako setAssociatedObject v Objective-C, to je hezky transparentní, kód zůstane čistý a všichni jsou spokojení. Jen teda asi nemáme žádný jazyk s traity a runtimem, který by to umožňoval. Třeba jednou vznikne nějaký Objective-Rust :)

BoneFlute

  • *****
  • 1 981
    • Zobrazit profil
Re:Trait a konstruktor
« Odpověď #7 kdy: 18. 12. 2020, 19:08:12 »
Co se týče námitky, zda do traitů patří data, a tak.

V jednom z mejch DSL mám něco takového:

Kód: [Vybrat]
Person = {
    name: String
    surname: String
}
ACL = {
    roles: [Role]
    permissions: [Permission]
    extraPermissions: [Permission]
}

UserAcount = {
    login: String
    password: Password
    email: Email
    ... Person
    ... ACL
}

Přijde mi to celkem užitečné a zpřehledňující. Co byste tomu vytkly?

Jak byste řešili konstruktory?

BoneFlute

  • *****
  • 1 981
    • Zobrazit profil
Re:Trait a konstruktor
« Odpověď #8 kdy: 18. 12. 2020, 19:13:32 »
V Rustu, pokud vím, trait nemá fieldy. Proč je tam potřebuješ mít, patří tam vůbec?

Předpokládejme, že ano.

Motivace traitů, tak jak o ní v tomto vlákně mluvím, je komponovat "objekt" z částí. Tedy něco na co se zneužívá dědičnost, ale bez nevýhod dědičnosti.

BoneFlute

  • *****
  • 1 981
    • Zobrazit profil
Re:Trait a konstruktor
« Odpověď #9 kdy: 18. 12. 2020, 19:17:35 »
class C(val f: Integer) extends T {
  def printF(): Unit = {
    println(f)
  }
}
Tohle je docela hezký.

Jde nějak ještě když bych tomu chtěl přidat nějakou logiku? Tedy konstruktor přijme f, přepočítá ho, a teprve výsledek uloží do fieldu f?

Idris

  • *****
  • 2 286
    • Zobrazit profil
    • E-mail
Re:Trait a konstruktor
« Odpověď #10 kdy: 18. 12. 2020, 19:18:39 »
Motivace traitů, tak jak o ní v tomto vlákně mluvím, je komponovat "objekt" z částí. Tedy něco na co se zneužívá dědičnost, ale bez nevýhod dědičnosti.
Takže něco jako implicitní kompozice v Go?

BoneFlute

  • *****
  • 1 981
    • Zobrazit profil
Re:Trait a konstruktor
« Odpověď #11 kdy: 18. 12. 2020, 19:43:44 »
Motivace traitů, tak jak o ní v tomto vlákně mluvím, je komponovat "objekt" z částí. Tedy něco na co se zneužívá dědičnost, ale bez nevýhod dědičnosti.
Takže něco jako implicitní kompozice v Go?

Go moc dobře neznám. Ale vypadá to, že ano.

Idris

  • *****
  • 2 286
    • Zobrazit profil
    • E-mail
Re:Trait a konstruktor
« Odpověď #12 kdy: 18. 12. 2020, 20:23:27 »
Motivace traitů, tak jak o ní v tomto vlákně mluvím, je komponovat "objekt" z částí. Tedy něco na co se zneužívá dědičnost, ale bez nevýhod dědičnosti.
Takže něco jako implicitní kompozice v Go?
Go moc dobře neznám. Ale vypadá to, že ano.
To by dávalo smysl a je to v podstatě jednoduché na implementaci i používání.

Re:Trait a konstruktor
« Odpověď #13 kdy: 18. 12. 2020, 20:45:10 »
V Rustu, pokud vím, trait nemá fieldy. Proč je tam potřebuješ mít, patří tam vůbec?

Předpokládejme, že ano.

Motivace traitů, tak jak o ní v tomto vlákně mluvím, je komponovat "objekt" z částí. Tedy něco na co se zneužívá dědičnost, ale bez nevýhod dědičnosti.

Eee?! Tomu co vidím tady https://forum.root.cz/index.php?topic=24024.msg341967#msg341967 se říká kompozice a traity k tomu nepotřebuju. Traity mi k objektu přimíchají další chování aniž bych si musel uložit do fieldu jejich instance.

Nebo mi něco uniká?

Souhlasím, že construktory by měly být součástí rozhraní. Vlastně bych kostruktory nejradši zrušil a zavedl místo nich kategorii pro faktory metody a těm umožnil být (volitelně) součástí do rozhraní/traitu. Fieldy mi v traitech nevadí.  S dynamických dispatch by to byl super jazyk.

Pragmaticky a přitom docela dobře to má udělané Groovy http://docs.groovy-lang.org/next/html/documentation/core-traits.html

BoneFlute

  • *****
  • 1 981
    • Zobrazit profil
Re:Trait a konstruktor
« Odpověď #14 kdy: 18. 12. 2020, 22:44:38 »
V Rustu, pokud vím, trait nemá fieldy. Proč je tam potřebuješ mít, patří tam vůbec?

Předpokládejme, že ano.

Motivace traitů, tak jak o ní v tomto vlákně mluvím, je komponovat "objekt" z částí. Tedy něco na co se zneužívá dědičnost, ale bez nevýhod dědičnosti.

Eee?! Tomu co vidím tady https://forum.root.cz/index.php?topic=24024.msg341967#msg341967 se říká kompozice a traity k tomu nepotřebuju. Traity mi k objektu přimíchají další chování aniž bych si musel uložit do fieldu jejich instance.

Ee, kompozice v kontextu OOP je trochu něco jiného. Já chci, aby výsledek bylo toto:

Kód: [Vybrat]
UserAcount = {
    login: String
    password: Password
    email: Email
    name: String
    surname: String
    roles: [Role]
    permissions: [Permission]
    extraPermissions: [Permission]
}
Jestli tomu budeme říkat traity, nebo jinak je mi celkem jedno.

Zatím jsme tu probrali, zda to je či není dobrý nápad.

Takže ještě k mé otázce - jak tyto vlastnosti inicializovat, případně když ta inicializace má nějakou komplexní logiku. Proč by to měl být problém souvisí poněkud s tím, že ty fieldy z traitu jsou privátní, mají nějaký provázaný stav, který by si měl hlídat ten trait.

Souhlasím, že construktory by měly být součástí rozhraní. Vlastně bych kostruktory nejradši zrušil a zavedl místo nich kategorii pro faktory metody a těm umožnil být (volitelně) součástí do rozhraní/traitu. Fieldy mi v traitech nevadí.  S dynamických dispatch by to byl super jazyk.

Pragmaticky a přitom docela dobře to má udělané Groovy http://docs.groovy-lang.org/next/html/documentation/core-traits.html

Na to groovy jsem koukal, a nenašel jsem tam ukázku, jak se ty fieldy inicializují.

Pro demonstraci předpokládáme situaci, kdy do existující třídy přidáme nový trait, takže musím přepsat konstruktory té třídy, to se má samo sebou. Otázka je, jak to bude vypadat.