Rust - std::ANY alebo lepší návrh?

Re:Rust - std::ANY alebo lepší návrh?
« Odpověď #120 kdy: 02. 12. 2021, 17:50:20 »

Nebo z PHP světa, composer: nastavím si balíček, že má být ve verzi vendor/balicek:1.*, a on mi vygeneruje graf závislostí, kde bude jinejvendor/jinejbalicek:1.8.6, ale já vím, že zrovna tento má chybu, a potřebuju nižší/vyšší. Tak to poedituju/přinutím mu dát konkrétní verzi, a zase dám composerem zkontrolovat závislosti a vygenerovat graf.

V php sem uz dloooouho nic nedelal takze si nejsem jistej v kramflecich, ten graf zavislosti je potreba commitovat?
Ale obcene bych nepovazoval konfiguraci dependency managementu za "kod".
Takze tam jsem OK s tim ze se kousek nejak generuje a kousek pise rucne.
Ten graf závislostí je třeba commitovat (není to specifikum php, je to principielní problém), protože ty máš v jednom souboru uvedený cca verze a balíčky, které chceš, zatímco v tom druhém souboru jsou už přesné verze všech balíčků. Aby se pokaždé vygeneroval stejný graf a stáhly se vždycky ty samé balíčky - důsledky si asi domyslíš.

No nejakou dobu uz sem to nemusel resit ale pokud si pamatuju dobre tak maven a leiningen oba funguji tak ze kdyz uvedu explixitni zavislost primo na nejake verzi tak se ta verze pouzije i pro tranzitivni zavislosti.
(Myslim, ze aspon driv maven fungoval tak ze ta explicitni verze musela byt uvedena "nahore", coz je trochu napalici, ale nevim jestli to tak porad je).

Graf si sice muzu vygenerovat ale nikam ho nekomitnu, protoze to stejne pri buildu nikdo nepouzije.




BoneFlute

  • *****
  • 1 981
    • Zobrazit profil
Re:Rust - std::ANY alebo lepší návrh?
« Odpověď #121 kdy: 02. 12. 2021, 18:02:07 »
Graf si sice muzu vygenerovat ale nikam ho nekomitnu, protoze to stejne pri buildu nikdo nepouzije.
Graf ne, ale informace o těch konkrétních závislostech ano. Chceš tři balíčky s nějakou cca verzí - dostaneš jich stovku s konkrétní verzí. Zkontroluješ, zda všechno funguje a tím, že to commitneš (tu stovku balíčků se specifickými závislostmy) tak uděláš schválení, že takto je to správně.

Re:Rust - std::ANY alebo lepší návrh?
« Odpověď #122 kdy: 02. 12. 2021, 18:05:39 »

Přesně takto se to používá:
1/ Generovaný kód, jehož znění nevyžaduje schválení člověkem = generuje se na CI serveru (nebo kdekoliv jinde) + necommituje se.
2/ Generovaný kód, který mi vygeneruje stroj, ale musí projít schválením člověkem = generuje se u vývojáře, a pak se commituje.

Má to tyto důsledky:
V prvním případě se jede na tvrdo optimalizace, stroj může cokoliv.
V druhém případě je očekáváno, že to co se vygeneruje musí být co nejvíce čitelné; nejlépe optimalizované pro diff. (A následně se to třeba celé ještě projede první variantou.)

TypoProvidery a podobně jsou jen první varianta. Jen už zakomponovaná do překladače. Jak už tu někdo zmínil. Ve skutečnosti je to ale spousta dalších případů: gcc, javac, scalac, ... etc, kde by tě ani nepadlo uvažovat, že je to jen prachsprostej generátor kódu. A ne, není to něco jiného :)

Mel bys nejakej netrivialni priklad 2?

Příklad 3: VisualStudio má návrháře UI. Můžeš si tam naklikat, natahat, naarangovat elementy. Ty se potom zapíšou do souboru. Buď do C#, nebo do XAML. V případě XAML se tento ještě zpracovává do výsledného něčeho. C# je part třída, kde máš jednu část, kterou má na starost návrhář, a druhou část, kterou používáš ty jako vývojář pro vlastní opičárny. Když hrábneš do té návrhářské třídy, tak pokud to nepřeženeš, tak se to zpětně promítne do toho návrháře. Osvědčilo se mi do té návrhářské třídy koukat. Lépe si všimnu, že jsem udělal nějakou nepravost. Nebo naopak, při přejmenování nějakého elementu, se přejmenuje i tady a návrhář to v pohodě zkousne.

Hmmm s tim sem nikdy nedelal.
Pamatuju si nejaky takovy udelatko pro Qt a jestli se nepletu tak tam se to do jednoho souboru nemotalo.
Tam byl snad v tom navrhari nejaky dialog pro event binding nebo co... to se mi zda lepsi.
Navic vystup z toho snad bylo nejaky xml a ne primo trida v cilovem jazyce.
Ale kdo vi... uz je to davno

Navic tady jsme trochu dal od generovani neceho ze schematu.
Tady "programator" "programuje". Sice tahanim nejakejch policek po formulari, ale to je jedno... to ze existuje kod a jeho visualni reprezentace je v poradku. Je to jedinny "zdroj pravdy" tak je v poradku ze to commitnu.

Re:Rust - std::ANY alebo lepší návrh?
« Odpověď #123 kdy: 02. 12. 2021, 18:13:19 »
Graf si sice muzu vygenerovat ale nikam ho nekomitnu, protoze to stejne pri buildu nikdo nepouzije.
Graf ne, ale informace o těch konkrétních závislostech ano. Chceš tři balíčky s nějakou cca verzí - dostaneš jich stovku s konkrétní verzí. Zkontroluješ, zda všechno funguje a tím, že to commitneš (tu stovku balíčků se specifickými závislostmy) tak uděláš schválení, že takto je to správně.

to fakt v mavenu ani v leiningenu nedelam... A nebo te porad spatne chapu.

Zavisim na balicku A:1.2.3 co zavisi na X, Y, Z.
Y zavisi na P ve verzi 3.14 a ten ma bug.
Moje zavislosti budou:
  • P:3.1415 kde uz vim ze je bug opraven a
  • A

Maven sam spravne dotahne i X, Y, a Z a P tam budu mit ve verzi 3.1415

ale kdyz se podivam do repositare nikde nenajdu ani zminku o X, Y a Z

BoneFlute

  • *****
  • 1 981
    • Zobrazit profil
Re:Rust - std::ANY alebo lepší návrh?
« Odpověď #124 kdy: 02. 12. 2021, 18:25:35 »

Přesně takto se to používá:
1/ Generovaný kód, jehož znění nevyžaduje schválení člověkem = generuje se na CI serveru (nebo kdekoliv jinde) + necommituje se.
2/ Generovaný kód, který mi vygeneruje stroj, ale musí projít schválením člověkem = generuje se u vývojáře, a pak se commituje.

Má to tyto důsledky:
V prvním případě se jede na tvrdo optimalizace, stroj může cokoliv.
V druhém případě je očekáváno, že to co se vygeneruje musí být co nejvíce čitelné; nejlépe optimalizované pro diff. (A následně se to třeba celé ještě projede první variantou.)

TypoProvidery a podobně jsou jen první varianta. Jen už zakomponovaná do překladače. Jak už tu někdo zmínil. Ve skutečnosti je to ale spousta dalších případů: gcc, javac, scalac, ... etc, kde by tě ani nepadlo uvažovat, že je to jen prachsprostej generátor kódu. A ne, není to něco jiného :)

Mel bys nejakej netrivialni priklad 2?

Příklad 3: VisualStudio má návrháře UI. Můžeš si tam naklikat, natahat, naarangovat elementy. Ty se potom zapíšou do souboru. Buď do C#, nebo do XAML. V případě XAML se tento ještě zpracovává do výsledného něčeho. C# je part třída, kde máš jednu část, kterou má na starost návrhář, a druhou část, kterou používáš ty jako vývojář pro vlastní opičárny. Když hrábneš do té návrhářské třídy, tak pokud to nepřeženeš, tak se to zpětně promítne do toho návrháře. Osvědčilo se mi do té návrhářské třídy koukat. Lépe si všimnu, že jsem udělal nějakou nepravost. Nebo naopak, při přejmenování nějakého elementu, se přejmenuje i tady a návrhář to v pohodě zkousne.

Hmmm s tim sem nikdy nedelal.
Pamatuju si nejaky takovy udelatko pro Qt a jestli se nepletu tak tam se to do jednoho souboru nemotalo.
Tam byl snad v tom navrhari nejaky dialog pro event binding nebo co... to se mi zda lepsi.
Navic vystup z toho snad bylo nejaky xml a ne primo trida v cilovem jazyce.
Ale kdo vi... uz je to davno

Navic tady jsme trochu dal od generovani neceho ze schematu.
Tady "programator" "programuje". Sice tahanim nejakejch policek po formulari, ale to je jedno... to ze existuje kod a jeho visualni reprezentace je v poradku. Je to jedinny "zdroj pravdy" tak je v poradku ze to commitnu.
Já jsem se nebavil o generování ze schematu. Já jsem obhajoval "generovat kód vždy a všude" v opozici ke "generování kódu je zlo" :-)

Graf si sice muzu vygenerovat ale nikam ho nekomitnu, protoze to stejne pri buildu nikdo nepouzije.
Graf ne, ale informace o těch konkrétních závislostech ano. Chceš tři balíčky s nějakou cca verzí - dostaneš jich stovku s konkrétní verzí. Zkontroluješ, zda všechno funguje a tím, že to commitneš (tu stovku balíčků se specifickými závislostmy) tak uděláš schválení, že takto je to správně.

to fakt v mavenu ani v leiningenu nedelam... A nebo te porad spatne chapu.

Zavisim na balicku A:1.2.3 co zavisi na X, Y, Z.
Y zavisi na P ve verzi 3.14 a ten ma bug.
Moje zavislosti budou:
  • P:3.1415 kde uz vim ze je bug opraven a
  • A

Maven sam spravne dotahne i X, Y, a Z a P tam budu mit ve verzi 3.1415

ale kdyz se podivam do repositare nikde nenajdu ani zminku o X, Y a Z


OK, Maven to dělá jinak, v pořádku.

Možná jde o to, že u Composeru (a nejen u něj), balíček nezávisí na konkrétní verzi. Takže když by byla vydána nová verze nějakého balíčku, tak by se mohlo stát, že se tam stáhne ona. A to pochopitelně nechci.
Jak to dělá Maven netuším, ale předpokládám, že je tam nějaká chytristika, aby po konfiguraci bylo zaručeno, že se vždy vytvoří stejný graf.
« Poslední změna: 02. 12. 2021, 18:30:15 od BoneFlute »


Re:Rust - std::ANY alebo lepší návrh?
« Odpověď #125 kdy: 02. 12. 2021, 19:06:58 »

Já jsem se nebavil o generování ze schematu. Já jsem obhajoval "generovat kód vždy a všude" v opozici ke "generování kódu je zlo" :-)

Jasne. Tady sme myslim na hrane, ale uznam ti ze se jedna o generovani. A uznam, ze tenhle generovany kod je potreba commitnout.
Ale ta rucni editace k tomu se mi uz fakt nelibi...


OK, Maven to dělá jinak, v pořádku.

Možná jde o to, že u Composeru (a nejen u něj), balíček nezávisí na konkrétní verzi. Takže když by byla vydána nová verze nějakého balíčku, tak by se mohlo stát, že se tam stáhne ona. A to pochopitelně nechci.
Jak to dělá Maven netuším, ale předpokládám, že je tam nějaká chytristika, aby po konfiguraci bylo zaručeno, že se vždy vytvoří stejný graf.

Uz mozna rozumim...
U mavenu mam snapshot a release artefakty. Release uz se nemuze zmenit takze i jeho tranzitivni zavislosti budou stejne.
Snapshot se meni jak sam tak jeho zavislosti... tam by to davalo smysl.
A to je mozna duvod proc na to nenarazim.
Snapshotum se obloukem vyhybam...


V leiningenu myslim muzu pouzivat version ranges... ale je u toho nejaky warning, ze se to nema delat....

BoneFlute

  • *****
  • 1 981
    • Zobrazit profil
Re:Rust - std::ANY alebo lepší návrh?
« Odpověď #126 kdy: 02. 12. 2021, 19:11:01 »
Snapshotum se obloukem vyhybam...

V leiningenu myslim muzu pouzivat version ranges... ale je u toho nejaky warning, ze se to nema delat....
Uváděl jsem příklad na generování kódu. Zda je to dobrý nápad řešit verzování a závislosti knihoven takhle nebo jinak, to je už trochu jinej příběh. :-)

Idris

  • *****
  • 2 286
    • Zobrazit profil
    • E-mail
Re:Rust - std::ANY alebo lepší návrh?
« Odpověď #127 kdy: 02. 12. 2021, 22:56:08 »
pokud chcete ORM, alternativni pristup je pouzit reflexi z metadat
To závisí na možnost jazyka. Pokud to jde jen initonly reflexí, tak proč ne? Třeba Go takto řeší spoustu věcí (kromě ORM třeba REST API). Kód není úplně hezký (hlavně ten pro runtime proper), ale je rychlý a bezpečný.

Re:Rust - std::ANY alebo lepší návrh?
« Odpověď #128 kdy: 11. 01. 2022, 22:08:44 »
Ahojte Kolegovia, ozivujem temu, nakolko pribudli skusenosti ohladom Rustu, tak davam na zretel lepsi navrh :)
za pozornost stoji "create_table" v "impl Database" (1)
pripadne "pub fn push<T: CreateTable + ToDatabase>(&mut self, data: &T) " v (2)

Samozrejme je mozne nahradit Vec -> HashSet - to je kazdeho volba :)
Kód: [Vybrat]
/* temp */
/* use MYSQL::Connection; */
pub struct Connection {}
impl Connection {
    pub fn execute(/* ... */){/* ... */}
}

pub enum DatabaseMembers {
    X,
    Y,
    Z
}

// Various Structs
pub struct X {
    pub name: String
}

pub struct Y {
    pub text: String
}

pub struct Z {
    pub value: u64
}

/* Traits */

pub trait CreateTable {
    fn create_table(c: &Connection) -> Result<(), Error>;
    fn table_type() -> DatabaseMembers;
}

pub trait FromDatabase : Sized {
    fn get(c: &Connection) -> Result<Self, Error>;
}

pub trait ToDatabase {
    fn push(c: &Connection, data: &Self) -> Result<(), Error>;
}

/* Trait implementation */

/* Impl for X */
impl CreateTable for X {
    fn table_type() -> DatabaseMembers{
        DatabaseMembers::X;
    }

    fn create_table(c: &Connection) -> Result<(), Error>{
        let query =
        "CREATE TABLE IF NOT EXISTS X (
            id      INTEGER PRIMARY KEY,
            text    TEXT NOT NULL,
        );";

        Ok(())
    }
}

impl ToDatabase for X {
    fn push(c: &Connection, data: &Self) -> Result<(), PError> {
        let query =
        "INSERT INTO ...
        (name, ...)
        VALUES
        (?1, ...)
        ";
    }
}

impl FromDatabase for X {
    fn get(c: &Connection, id: usize) -> Result<Self, Error>{
        let query =
        "SELECT *
        FROM ...
        WHERE ... id=?1
        ";
        /* ... */
        Ok(X{ /*... */})
    }
}
/* Impl for Y */
impl CreateTable for Y {
    fn table_type() -> DatabaseMembers{
        DatabaseMembers::Y;
    }

    fn create_table(c: &Connection) -> Result<(), Error>{
        let query =
        "CREATE TABLE IF NOT EXISTS Y (
            id      INTEGER PRIMARY KEY,
            text    TEXT NOT NULL,
        );";

        Ok(())
    }
}

impl ToDatabase for Y {
    fn push(c: &Connection, data: &Self) -> Result<(), PError> {
        let query =
        "INSERT INTO ...
        (name, ...)
        VALUES
        (?1, ...)
        ";
    }
}

impl FromDatabase for Y {
    fn get(c: &Connection, id: usize) -> Result<Self, Error>{
        let query =
        "SELECT *
        FROM ...
        WHERE ... id=?1
        ";
        /* ... */
        Ok(Y{ /*... */})
    }
}

/* Impl for Z */
/* ... */

pub struct Database {
    c: Connection,
    initialized_tables: Vec<DatabaseTables>,
}

impl Database {
    pub fn open_temporary() -> Database {
        let mut db = Database {
            c: Connection::open_in_memory(),
        }
        /* ... */
        Ok(db)
    }

    /* (1) - Explicitne vytvorim tabulku ...*/
    pub fn create_table<T: CreateTable>(&mut self) -> Result<(), PError> {
        if !self.initialized_tables.contains(&T::table_type()) {
            let dbtable = T::create_table(&self.conn)?;
            self.initialized_tables.push(dbtable);
        }   

        Ok(())
    }

    pub fn push<T: CreateTable + ToDatabase>(&mut self, data: &T) {
        /* (2) - alebo implicitne vytvorim tabulku */
        if !self.initialized_tables.contains(&T::table_type()) {
            T::create_table(&self.conn)?;
            self.initialized_tables.push(&T::table_type());
        }
        ToDatabase::push(&self.conn, data)
    }

    pub fn get<T: FromDatabase>(&self, id: usize) -> Result<T, PError>{
        FromDatabase::get(&self.conn, uid)
    }

}

fn main() {

    let mut db: Database = Database::open_temporary();

    /* (1) - explicitne vytvorim tabulku pre X */
    db.create_table::<X>();
    let x = X {name: String::from("Ahoj")};
    db.push(&x);

    // (2) - implicitne vytvorim tabulku pre Y */
    let y = Y {text: String::from("Ahoj")};
    db.push(&y);
}

Pekny vecer

Idris

  • *****
  • 2 286
    • Zobrazit profil
    • E-mail
Re:Rust - std::ANY alebo lepší návrh?
« Odpověď #129 kdy: 15. 01. 2022, 10:50:10 »
Ahojte Kolegovia, ozivujem temu, nakolko pribudli skusenosti ohladom Rustu, tak davam na zretel lepsi navrh :)
za pozornost stoji "create_table" v "impl Database" (1)
pripadne "pub fn push<T: CreateTable + ToDatabase>(&mut self, data: &T) " v (2)

Samozrejme je mozne nahradit Vec -> HashSet - to je kazdeho volba :)
Kód: [Vybrat]
/* temp */
/* use MYSQL::Connection; */
pub struct Connection {}
impl Connection {
    pub fn execute(/* ... */){/* ... */}
}

pub enum DatabaseMembers {
    X,
    Y,
    Z
}

// Various Structs
pub struct X {
    pub name: String
}

pub struct Y {
    pub text: String
}

pub struct Z {
    pub value: u64
}

/* Traits */

pub trait CreateTable {
    fn create_table(c: &Connection) -> Result<(), Error>;
    fn table_type() -> DatabaseMembers;
}

pub trait FromDatabase : Sized {
    fn get(c: &Connection) -> Result<Self, Error>;
}

pub trait ToDatabase {
    fn push(c: &Connection, data: &Self) -> Result<(), Error>;
}

/* Trait implementation */

/* Impl for X */
impl CreateTable for X {
    fn table_type() -> DatabaseMembers{
        DatabaseMembers::X;
    }

    fn create_table(c: &Connection) -> Result<(), Error>{
        let query =
        "CREATE TABLE IF NOT EXISTS X (
            id      INTEGER PRIMARY KEY,
            text    TEXT NOT NULL,
        );";

        Ok(())
    }
}

impl ToDatabase for X {
    fn push(c: &Connection, data: &Self) -> Result<(), PError> {
        let query =
        "INSERT INTO ...
        (name, ...)
        VALUES
        (?1, ...)
        ";
    }
}

impl FromDatabase for X {
    fn get(c: &Connection, id: usize) -> Result<Self, Error>{
        let query =
        "SELECT *
        FROM ...
        WHERE ... id=?1
        ";
        /* ... */
        Ok(X{ /*... */})
    }
}
/* Impl for Y */
impl CreateTable for Y {
    fn table_type() -> DatabaseMembers{
        DatabaseMembers::Y;
    }

    fn create_table(c: &Connection) -> Result<(), Error>{
        let query =
        "CREATE TABLE IF NOT EXISTS Y (
            id      INTEGER PRIMARY KEY,
            text    TEXT NOT NULL,
        );";

        Ok(())
    }
}

impl ToDatabase for Y {
    fn push(c: &Connection, data: &Self) -> Result<(), PError> {
        let query =
        "INSERT INTO ...
        (name, ...)
        VALUES
        (?1, ...)
        ";
    }
}

impl FromDatabase for Y {
    fn get(c: &Connection, id: usize) -> Result<Self, Error>{
        let query =
        "SELECT *
        FROM ...
        WHERE ... id=?1
        ";
        /* ... */
        Ok(Y{ /*... */})
    }
}

/* Impl for Z */
/* ... */

pub struct Database {
    c: Connection,
    initialized_tables: Vec<DatabaseTables>,
}

impl Database {
    pub fn open_temporary() -> Database {
        let mut db = Database {
            c: Connection::open_in_memory(),
        }
        /* ... */
        Ok(db)
    }

    /* (1) - Explicitne vytvorim tabulku ...*/
    pub fn create_table<T: CreateTable>(&mut self) -> Result<(), PError> {
        if !self.initialized_tables.contains(&T::table_type()) {
            let dbtable = T::create_table(&self.conn)?;
            self.initialized_tables.push(dbtable);
        }   

        Ok(())
    }

    pub fn push<T: CreateTable + ToDatabase>(&mut self, data: &T) {
        /* (2) - alebo implicitne vytvorim tabulku */
        if !self.initialized_tables.contains(&T::table_type()) {
            T::create_table(&self.conn)?;
            self.initialized_tables.push(&T::table_type());
        }
        ToDatabase::push(&self.conn, data)
    }

    pub fn get<T: FromDatabase>(&self, id: usize) -> Result<T, PError>{
        FromDatabase::get(&self.conn, uid)
    }

}

fn main() {

    let mut db: Database = Database::open_temporary();

    /* (1) - explicitne vytvorim tabulku pre X */
    db.create_table::<X>();
    let x = X {name: String::from("Ahoj")};
    db.push(&x);

    // (2) - implicitne vytvorim tabulku pre Y */
    let y = Y {text: String::from("Ahoj")};
    db.push(&y);
}

Pekny vecer
U těch selectů by bylo hezké mít je typově bezpečné (native queries), Rust je jedním z mála jazyků, kde to jde přirozeně.