00:05 Introduzione
02:50 Due illusioni
09:09 Una pipeline del compilatore
14:23 La storia finora
18:49 Polizza di carico
19:40 Progettazione del linguaggio
23:52 Il futuro
30:35 Il passato
35:57 Scegliere le battaglie
39:45 Grammatiche 1/3
42:41 Grammatiche 2/3
49:02 Grammatiche 3/3
53:02 Analisi statica 1/2
58:50 Analisi statica 2/2
01:04:55 Sistema di tipi
01:11:59 Interni del compilatore
01:27:48 Ambiente di esecuzione
01:33:57 Conclusione
01:36:33 Prossima lezione e domande del pubblico
Descrizione
La maggior parte dei supply chains è ancora gestita tramite fogli di calcolo (cioè Excel), mentre i sistemi aziendali sono in uso da una, due, a volte tre decadi - presumibilmente per sostituirli. Infatti, i fogli di calcolo offrono un’espressività programmatica accessibile, mentre quei sistemi generalmente no. Più in generale, dagli anni ‘60, si assiste a uno sviluppo costante sia dell’industria del software nel suo complesso sia dei suoi linguaggi di programmazione. Ci sono evidenze che la prossima fase delle supply chain performance sarà in gran parte guidata dallo sviluppo e dall’adozione dei linguaggi di programmazione, o meglio degli ambienti programmabili.
Trascrizione completa
Benvenuti a questa serie di lezioni sulle supply chains. Sono Joannes Vermorel, e oggi presenterò “Languages and Compilers for Supply Chain.” Non ricordo di aver mai visto i compilatori trattati in alcun manuale di supply chain, eppure l’argomento è di primaria importanza. Abbiamo visto, nel primissimo capitolo di questa serie, nella lezione intitolata “Product-Oriented Delivery,” che la programmazione è la chiave per capitalizzare il tempo investito dagli esperti di supply chain. Senza programmazione, non possiamo capitalizzare il loro tempo, e trattiamo gli esperti di supply chain come risorse usa e getta.
Inoltre, in questo quarto capitolo, attraverso le due lezioni precedenti “Mathematical Optimization for Supply Chain” e “Machine Learning for Supply Chain,” abbiamo visto che questi due campi - ottimizzazione e apprendimento - sono diventati trainati dai paradigmi di programmazione nell’ultimo decennio, anziché rimanere un insieme di algoritmi e modelli come avveniva in passato. Tutti questi elementi evidenziano l’importanza primaria dei linguaggi di programmazione e, di conseguenza, sorge la questione di un linguaggio di programmazione e di un compilatore progettati per affrontare le supply chain challenges.
Lo scopo di questa lezione è demistificare la progettazione di un tale linguaggio di programmazione destinato agli scopi delle supply chain. Probabilmente la vostra azienda non svilupperà mai un proprio linguaggio di programmazione. Tuttavia, avere una certa conoscenza dell’argomento è fondamentale per poter valutare l’adeguatezza degli strumenti che possedete, e per valutare l’adeguatezza degli strumenti che intendete acquisire per affrontare le supply chain challenges che la vostra azienda deve fronteggiare. Inoltre, questa lezione dovrebbe anche aiutarvi a evitare alcuni dei grandi errori che le persone senza alcuna conoscenza in questo ambito tendono a commettere.
Cominciamo sfatando due illusioni che sono state prevalenti nei circoli del enterprise software negli ultimi tre decenni circa. La prima è l’illusione del “Lego programming”, in cui la programmazione viene vista come qualcosa che può essere completamente evitata. Infatti, la programmazione è difficile, e ci sono sempre fornitori che promettono che, grazie al loro prodotto e alla loro tecnologia fantastica, la programmazione possa essere trasformata in un’esperienza visiva accessibile a chiunque, eliminando completamente tutte le difficoltà intrinseche alla programmazione, tanto da rendere l’esperienza simile al semplice assemblaggio di Lego, cosa che anche un bambino può fare.
Questo tentativo è stato ripetuto innumerevoli volte negli ultimi due decenni e ha sempre inevitabilmente fallito. Al massimo, i prodotti che dovevano offrire un’esperienza visiva si sono trasformati in linguaggi di programmazione ordinari, non particolarmente più facili da padroneggiare rispetto ad altri linguaggi. Questo è, a proposito, il motivo aneddotico per cui, ad esempio, nella serie di prodotti Microsoft esiste la linea “Visual”, come Visual Basic for Application e Visual Studio. Tutti quei prodotti visuali furono introdotti negli anni ‘90 con la speranza di trasformare la programmazione in un’esperienza puramente visiva con designer, in cui bastasse semplicemente trascinare e rilasciare. La realtà è che tutti quegli strumenti hanno infine ottenuto un successo notevole, ma oggi sono semplici linguaggi di programmazione abbastanza ordinari. Ciò che è rimasto delle componenti visive iniziali è minimo.
L’approccio Lego ha fallito perché, fondamentalmente, il collo di bottiglia non è rappresentato dalla difficoltà della sintassi della programmazione. È una difficoltà, ma minima, specialmente se confrontata con la padronanza dei concetti necessari ogni volta che si vuole implementare un’automazione sofisticata. La vostra mente diventa il collo di bottiglia, e la comprensione dei concetti in gioco è molto più significativa della sintassi.
La seconda illusione è quella del “Star Wars tech”, che consiste nel pensare che sia semplicemente facile connettere e utilizzare fantastici pezzi di tecnologia. Questa illusione è molto attraente sia per i fornitori che per i progetti interni. Essenzialmente, diventa allettante dire: “Ecco questo fantastico database NoSQL che possiamo semplicemente integrare”, oppure “ecco questo fantastico stack di deep learning che possiamo adottare”, oppure questo database a grafo, o questo framework distribuito attivo, ecc. Il problema con questo approccio è che si tratta la tecnologia come viene trattata in Star Wars. Quando hai una mano meccanica, l’eroe può semplicemente prenderla, e funziona. Ma nella realtà, i problemi di integrazione prevalgono.
Star Wars ignora il fatto che ci sarebbero una serie di problemi: prima, avresti bisogno di antibiotici, poi di una lunga riqualificazione della mano per potercela addirittura usare. Avresti anche bisogno di un programma di manutenzione per la mano, perché è meccanica, e cosa dire della fonte di energia, ecc. Tutti questi problemi vengono semplicemente ignorati. Basta collegare quel fantastico pezzo di tecnologia, e funziona. Non è così nella realtà. I problemi di integrazione dominano, e ad esempio, nelle grandi aziende software, la maggior parte degli ingegneri non lavora su pezzi di tecnologia eccezionalmente cool. La stragrande maggioranza della forza lavoro ingegneristica di quei grandi fornitori di software è dedicata esclusivamente all’integrazione di tutte le parti.
Quando hai moduli, componenti o app, hai bisogno di una piccola armata di ingegneri per incollare insieme tutte quelle parti e affrontare tutti i problemi che emergono durante l’integrazione. Anche una volta superate tutte le difficoltà, serve comunque un’ampia forza lavoro ingegneristica per gestire il fatto che, modificando una parte del sistema, si tendono a creare problemi in altre sezioni. Quindi, è necessaria questa forza lavoro per intervenire e risolvere tali problemi.
A proposito, come aneddoto, un altro problema che ho osservato con i pezzi di tecnologia cool è l’effetto Dunning-Kruger che essi generano tra gli ingegneri. Introduci un pezzo di tecnologia interessante nel tuo stack, e all’improvviso gli ingegneri, solo perché hanno iniziato a sperimentare con una tecnologia che comprendono a malapena, credono di essere improvvisamente esperti di AI o simili. Questo è un tipico caso dell’effetto Dunning-Kruger ed è fortemente correlato al numero di tecnologie innovative che inserisci nella tua soluzione. In conclusione, con queste due illusioni, vediamo che non possiamo davvero aggirare il problema della programmazione. Dobbiamo affrontarlo in modo funzionale, comprese le sue parti più difficili.
Detto ciò, l’aspetto interessante dei linguaggi di programmazione è che i fornitori di enterprise software continuano a reinventare, quasi per caso, i linguaggi di programmazione, e lo fanno continuamente. Infatti, nelle supply chain, esiste una necessità ferocemente sentita di configurabilità. Come abbiamo visto nelle lezioni precedenti, il mondo delle supply chain è variegato e i problemi sono numerosi e diversificati. Pertanto, quando si ha un prodotto software per supply chain, la necessità di configurabilità è estremamente intensa. In via aneddotica, è per questo che configurare un pezzo di software è tipicamente un progetto che dura mesi, se non anni, a causa dell’enorme complessità insita nella configurazione.
Le impostazioni di configurazione sono spesso complesse, non si limitano a pulsanti o caselle di controllo. Possono includere trigger, formule, cicli e ogni sorta di blocchi correlati. La situazione sfugge rapidamente al controllo, e quello che ne risulta è un linguaggio di programmazione emergente. Tuttavia, essendo un linguaggio emergente, tende ad essere di bassa qualità.
Progettare un vero linguaggio di programmazione è un compito ingegneristico ben consolidato. Esistono decine di libri sulle praticità della realizzazione di un compilatore di livello produttivo. Un compilatore è un programma che converte istruzioni, tipicamente testuali, in codice macchina o in una forma di istruzioni a livello inferiore. Ogni volta che esiste un linguaggio di programmazione, è coinvolto un compilatore. Ad esempio, in un foglio di calcolo Excel, ci sono formule, e Excel dispone di un suo compilatore per compilare ed eseguire quelle formule. Molto probabilmente, l’intero pubblico ha usato compilatori per tutta la propria vita professionale senza rendersene conto.
Nel diagramma si vede una pipeline tipica, e questo archetipo si adatta alla maggior parte dei linguaggi di programmazione di cui probabilmente avete sentito parlare, come Python, JavaScript, Java e C#. Tutti questi linguaggi hanno una pipeline essenzialmente simile a quella qui illustrata. In una pipeline del compilatore, si ha una serie di trasformazioni, e ad ogni stadio del processo esiste una rappresentazione che incapsula l’intero programma. Il metodo di progettazione di un compilatore prevede una serie di trasformazioni ben definite, e ad ogni stadio si lavora sull’intero programma, sebbene rappresentato in modo diverso.
L’idea è che ogni trasformazione gestisca un insieme ben definito di problemi. Si affrontano questi problemi per poi passare alla trasformazione successiva che risolve un altro aspetto del processo. Generalmente, ad ogni stadio ci si avvicina sempre di più al codice a livello macchina. Il compilatore inizia con lo script e procede con trasformazioni molto vicine alla sintassi del linguaggio di programmazione in questione. Durante queste prime trasformazioni, un compilatore tipico rileva errori sintattici che impediscono di trasformare uno script, che non rappresenta nemmeno un programma valido, in qualcosa di eseguibile. Torneremo su questo argomento più avanti nella lezione per esaminare più da vicino una pipeline di compilazione.
Questa lezione è la quinta del quarto capitolo di questa serie. Nel primo capitolo, ho presentato le mie idee sulla supply chain sia come campo di studio che come pratica. Nel secondo capitolo, ho esaminato le metodologie appropriate per affrontare le situazioni presenti nelle supply chains. Come abbiamo visto, la maggior parte delle situazioni nelle supply chains è piuttosto avversa, per cui occorrono strategie e metodologie che siano resilienti ai comportamenti antagonisti, sia interni che esterni all’azienda.
Il terzo capitolo è dedicato al personale delle supply chains, ed è interamente focalizzato sullo studio dei problemi stessi delle supply chains. Dobbiamo stare molto attenti a non confondere il problema che stiamo identificando con il tipo di soluzione che potremmo immaginare per risolverlo. Si tratta di due questioni distinte: è necessario separare il problema dalla soluzione.
Il quarto e attuale capitolo di questa serie di lezioni è dedicato alle scienze ausiliarie della supply chain. Queste scienze non sono la supply chain in sé, ma sono di grande aiuto alla pratica della supply chain moderna. Attualmente stiamo salendo la scala delle astrazioni. Abbiamo iniziato questo capitolo con la fisica del computing, poi con gli algoritmi, per passare al mondo del software. Abbiamo introdotto l’ottimizzazione matematica, di grande interesse per le supply chains e che, tra l’altro, costituisce le basi del machine learning.
Oggi introduciamo linguaggi e compilatori, essenziali per implementare qualsiasi paradigma di programmazione. Sebbene l’argomento dei linguaggi e dei compilatori possa sorprendere il pubblico, credo che a questo punto non debba esserlo troppo. Abbiamo visto che l’ottimizzazione matematica e il machine learning oggi devono essere affrontati attraverso paradigmi di programmazione, il che solleva la questione di come implementare tali paradigmi. Questo ci porta alla progettazione di un linguaggio di programmazione e del relativo compilatore di supporto, che è esattamente l’argomento di oggi.
Questo è un riassunto del resto della lezione. Inizieremo con osservazioni a livello generale sulla storia e sul mercato dei linguaggi di programmazione. Esamineremo industrie che rappresentano, a mio avviso, sia il futuro che il passato delle supply chains. Successivamente, ci addentreremo gradualmente nella progettazione dei linguaggi di programmazione e dei compilatori, procedendo verso questioni di livello inferiore e le tecnicalità coinvolte nella progettazione di un compilatore.
Dal 1950, migliaia di linguaggi di programmazione sono stati messi in produzione. Le stime variano, ma il numero di linguaggi di programmazione utilizzati a scopi produttivi probabilmente si aggira tra mille e diecimila. Molti di questi linguaggi sono semplici variazioni o cugini l’uno dell’altro, a volte persino solo la base di codice del compilatore biforcata in una direzione leggermente diversa. È notevole che diverse grandi aziende di software enterprise si siano espanse notevolmente grazie all’introduzione di linguaggi di programmazione creati internamente. Per esempio, SAP ha introdotto ABAP nel 1983, Salesforce ha introdotto Apex nel 2006, e perfino Microsoft ha iniziato prima di Windows ingegnerizzando l’Altair BASIC nel 1975.
Storicamente, coloro di voi in pubblico che sono abbastanza anziani da ricordare gli anni ‘90 potrebbero ricordare che i fornitori dell’epoca commercializzavano linguaggi di programmazione di terza e quarta generazione. La realtà è che esisteva una serie ben definita di generazioni — prima, seconda, terza, quarta, ecc. — fino alla quinta, dove fondamentalmente la comunità ha smesso di contare in termini di generazioni. Durante le prime tre o quattro decadi, tutti quei linguaggi di programmazione seguivano una progressione regolare verso livelli di astrazione più elevati. Tuttavia, alla fine degli anni ‘90, c’erano già molte più direzioni oltre al semplice aumento del grado di astrazione, a seconda del caso d’uso.
Realizzare un nuovo linguaggio di programmazione è stato fatto molte volte. È un campo di ingegneria consolidato, e interi libri sono dedicati all’argomento da un punto di vista molto pratico. L’ingegnerizzazione di un nuovo linguaggio di programmazione è una pratica molto più concreta e prevedibile rispetto, diciamo, all’esecuzione di un esperimento di data science. È quasi certo che otterrete il risultato desiderato se applicate l’ingegnerizzazione adeguata, considerando ciò che si sa oggi e ciò che è immediatamente disponibile come conoscenza.
Tutto ciò solleva davvero la domanda: e se ci fosse un linguaggio di programmazione specificamente ingegnerizzato per scopi supply chain? Infatti, potrebbe esserci un notevole potenziale in termini di produttività, affidabilità e persino prestazioni della supply chain.
Per affrontare questa domanda, dobbiamo dare uno sguardo al futuro. Fortunatamente per noi, il modo più semplice per guardare al futuro è esaminare un settore che è stato costantemente un decennio avanti rispetto a tutti gli altri per le ultime tre decadi circa, e quello è l’industria dei videogiochi. Questo è un settore molto grande al giorno d’oggi, e solo per darvi un’idea delle dimensioni, l’industria dei videogiochi ora corrisponde a due terzi di quella dell’aerospazio a livello mondiale in termini di dimensioni comparative, ed essa sta crescendo molto più rapidamente dell’aerospazio. Un decennio da ora, i videogiochi potrebbero addirittura essere più grandi dell’aerospazio.
La cosa interessante dell’industria dei videogiochi è che possiede una struttura ben consolidata. In primo luogo, abbiamo i game engine, con i due leader che sono Unity e Unreal. Questi game engine includono i componenti a basso livello di interesse per grafiche 3D super intensive e raffinate, e delineano il panorama per il livello di infrastruttura del vostro codice. Ci sono alcune aziende che ingegnerizzano prodotti molto complessi chiamati game engine, e questi motori vengono utilizzati dall’intera industria.
Successivamente, abbiamo gli studi di sviluppo di giochi, che sviluppano il codice di gioco per ciascun gioco. Il codice di gioco sarà una base di codice tipicamente specifica per il singolo gioco in sviluppo. Il game engine richiede ingegneri del software estremamente competenti e tecnicamente abili, ma che non necessariamente conoscono molto il settore dei videogiochi. Lo sviluppo del codice di gioco non richiede competenze tecniche pure così intensive. Tuttavia, gli ingegneri del software che sviluppano il codice di gioco devono comprendere il gioco su cui stanno lavorando. Il codice di gioco stabilisce la piattaforma per le meccaniche di gioco, ma non specifica i dettagli minori.
Questo compito è tipicamente gestito dai game designer, che non sono ingegneri del software, ma scrivono codice nei linguaggi di scripting messi a loro disposizione dal team di ingegneria che si occupa del codice di gioco. Abbiamo queste tre fasi: game engine, che coinvolgono ingegneri del software estremamente tecnici e specializzati nella creazione dei blocchi fondamentali; studi, che hanno team di ingegneria, tipicamente uno per gioco, che sviluppano il gioco come piattaforma per le meccaniche di gioco; e infine, game designer, che non sono ingegneri del software ma specialisti del settore, implementando il comportamento che renderà felici i giocatori, i clienti al termine della filiera.
Al giorno d’oggi, il codice di gioco è frequentemente reso accessibile alla fanbase, il che significa che i game designer possono scrivere regole e potenzialmente modificare il gioco, ma anche i fan, che sono semplici consumatori dei giochi, possono farlo. Ci sono alcuni aneddoti interessanti in questo settore. Ad esempio, il gioco Dota 2, incredibilmente di successo, è iniziato come una modifica di un gioco esistente. La prima versione, semplicemente denominata Dota, era una modifica puramente basata sulla fanbase del gioco World of Warcraft 3. Si può osservare che questo grado di configurabilità e programmabilità a livello delle regole di gioco è molto esteso, poiché è stato possibile, partendo da un gioco commerciale esistente, World of Warcraft 3, creare un gioco completamente nuovo, che poi è diventato un enorme successo commerciale grazie alla seconda versione. Ora, questo è interessante, e possiamo iniziare a riflettere, guardando all’industria dei videogiochi, cosa significhi per l’industria della supply chain.
Beh, potremmo pensare a quale tipo di parallelo trarre. Potremmo avere un supply chain engine che si occupa delle parti algoritmiche molto difficili, dell’infrastruttura a basso livello e dei blocchi tecnologici fondamentali, come l’ottimizzazione matematica e il machine learning. L’idea è che, per ogni singolo supply chain, sarebbe necessario un team di ingegneria per raccogliere tutti i dati rilevanti e integrare l’intero panorama applicativo.
Come prima fase, avremmo bisogno dell’equivalente dei game designer, che sarebbero degli specialisti supply chain. Questi specialisti non sono ingegneri del software, ma sono le persone che scriveranno, attraverso un codice semplificato, tutte le regole e le meccaniche necessarie per implementare l’ottimizzazione predittiva di interesse per il supply chain. L’industria dei videogiochi fornisce un esempio vivido di ciò che probabilmente accadrà nel settore supply chain nel prossimo decennio.
Finora, l’approccio dell’industria dei videogiochi applicato al supply chain rimane fantascienza, ad eccezione di poche aziende. Credo che la maggior parte di queste aziende siano clienti di Lokad. Tornando all’argomento di oggi, abbiamo visto in precedenti lezioni che Excel rimane il linguaggio di programmazione numero uno in questo settore. A proposito, in termini di linguaggio di programmazione, Excel è un linguaggio di programmazione funzionale reattivo, quindi è addirittura una categoria a sé stante.
Oggigiorno potreste sentire da fornitori che propongono di aggiornare i supply chains utilizzando qualche tipo di setup di data science. Tuttavia, la mia osservazione informale nell’ultimo decennio è che la stragrande maggioranza di queste iniziative ha fallito. Questo appartiene ormai al passato, e per capire il perché, dobbiamo iniziare a esaminare l’elenco dei linguaggi di programmazione coinvolti. Se guardiamo a Excel, vediamo che coinvolge essenzialmente due linguaggi di programmazione: le formule Excel e VBA. VBA non è nemmeno un requisito; si può andare lontano usando solo le funzioni VLOOKUP in Excel. Tipicamente, si usa solamente un linguaggio di programmazione, ed è accessibile ai non ingegneri del software.
D’altra parte, l’elenco dei linguaggi di programmazione necessari per replicare le capacità di Excel con un setup di data science è piuttosto esteso. Avremo bisogno di SQL, e potenzialmente di diversi dialetti di SQL, per accedere ai dati. Avremo bisogno di Python per implementare la logica di base. Tuttavia, Python da solo tende a essere lento, quindi potrebbe essere necessario un sotto-linguaggio come NumPy. A questo punto, non si sta ancora facendo nulla in termini di machine learning o di ottimizzazione matematica, quindi per una vera analisi numerica hardcore sarà necessario qualcos’altro, un altro linguaggio di programmazione a sé stante, come PyTorch, per esempio. Ora che avete tutti questi elementi, ci sono già parecchie parti mobili, quindi la configurazione dell’applicazione stessa sarà piuttosto complessa. Avrete bisogno di una configurazione, e questa configurazione sarà scritta con un altro linguaggio di programmazione, come JSON o XML. Si potrebbe sostenere che questi non sono linguaggi di programmazione super complessi, ma aggiungono comunque ulteriori complessità.
Ciò che accade quando si hanno così tante parti mobili è che solitamente è necessario un sistema di build, qualcosa che possa eseguire tutti i compilatori e le ricette banali necessarie per produrre il software. I sistemi di build hanno un loro linguaggio. L’approccio tradizionale è un linguaggio chiamato Make, ma ce ne sono molti altri. Inoltre, poiché Excel è in grado di mostrare i risultati, è necessaria una modalità per visualizzare le cose all’utente e per rendere visivi i dati. Questo verrà fatto con una combinazione di JavaScript, HTML e CSS, aggiungendo altri linguaggi all’elenco.
A questo punto, abbiamo un lungo elenco di linguaggi di programmazione, e un setup di produzione effettivo potrebbe essere ancora più complesso. Questo spiega perché la maggior parte delle aziende che ha tentato di adottare questa pipeline di data science nell’ultimo decennio ha fallito in modo schiacciante, rimanendo in pratica con Excel. Il motivo è che ciò comporta il dover padroneggiare quasi una dozzina di linguaggi di programmazione anziché uno solo, come nel caso di Excel. Non abbiamo neppure iniziato ad affrontare i problemi concreti del supply chain; ci siamo limitati a discutere le questioni tecniche legate all’inizio dell’implementazione.
Ora, iniziamo a pensare a come potrebbe essere un linguaggio di programmazione per il supply chain. In primo luogo, dobbiamo decidere cosa è parte integrante del linguaggio e cosa invece appartiene alle librerie. Infatti, con i linguaggi di programmazione, è sempre possibile delegare alcune capacità alle librerie. Ad esempio, osserviamo il linguaggio di programmazione C. È considerato un linguaggio di programmazione abbastanza a basso livello, e C non dispone di un garbage collector. Tuttavia, è possibile utilizzare un garbage collector di terze parti come libreria in un programma in C. A causa del fatto che la gestione automatica della memoria non è un elemento fondamentale nel linguaggio C, la sintassi tende a essere relativamente prolissa e tediosa.
Per scopi supply chain, esistono aspetti come l’ottimizzazione matematica e il machine learning che di solito sono trattati come librerie. Così, abbiamo un linguaggio di programmazione, e tutte queste preoccupazioni vengono essenzialmente delegate a librerie di terze parti. Tuttavia, se dovessimo ingegnerizzare un linguaggio di programmazione per il supply chain, avrebbe davvero senso che questi aspetti fossero integrati come elementi di prima classe all’interno del linguaggio stesso. Inoltre, avrebbe senso includere i dati relazionali come parte integrante del linguaggio, come primi cittadini. Nel supply chain, il panorama applicativo, che comprende molti pezzi di software enterprise, presenta dati relazionali sotto forma di database relazionali, come i database SQL, ovunque. Praticamente tutti i prodotti di software enterprise esistenti al giorno d’oggi hanno al loro interno un database relazionale, il che significa che per scopi supply chain, non appena vogliamo toccare i dati, in realtà interagiremo con dati di natura relazionale. I dati si presentano come un elenco di tabelle estratte da tutti quei database che alimentano le varie app, e ogni tabella ha un elenco di colonne o campi.
Ha davvero senso avere i dati relazionali integrati nel linguaggio. Inoltre, che dire dell’interfaccia utente (UI) e dell’esperienza utente (UX)? Uno dei punti di forza di Excel è che tutto ciò è completamente incorporato nel linguaggio, così non si ha un linguaggio di programmazione separato da tutte le librerie di terze parti necessarie per gestire la presentazione, il rendering e l’interazione con l’utente. Tutto ciò fa parte del linguaggio. Rendere tutto ciò un primo cittadino sarebbe di grande interesse per il supply chain, almeno se vogliamo essere all’altezza di Excel per quanto riguarda i supply chain.
Ora, nella progettazione dei linguaggi, la grammatica rappresenta la rappresentazione formale delle regole che definiscono un programma valido secondo il nuovo linguaggio di programmazione introdotto. Essenzialmente, si parte da un pezzo di testo, e prima si applica un lexer, che è una specifica classe di algoritmi o un piccolo programma. Il lexer decompone il pezzo di testo, il programma che avete appena scritto, in una sequenza di token. Il lexer isola tutte le variabili e i simboli in gioco nel vostro linguaggio di programmazione. La grammatica aiuta a convertire la sequenza di token nella semantica effettiva del programma, definendo cosa significhi il programma e il preciso set non ambiguo di operazioni necessarie per eseguirlo.
La grammatica stessa viene tipicamente affrontata come un compromesso tra le problematiche che si desidera internalizzare all’interno del linguaggio e i concetti che si vuole esternalizzare. Ad esempio, se si considera i dati relazionali come una preoccupazione esterna, il programmatore dovrebbe introdurre molte strutture dati specializzate come dizionari, lookup e tabelle hash per eseguire manualmente all’interno del linguaggio tutte quelle operazioni. Se invece la grammatica vuole internalizzare l’algebra relazionale, significa che il programmatore può scrivere normalmente tutta la logica relazionale direttamente nella sua forma relazionale. Tuttavia, ciò implica che improvvisamente tutti quei vincoli relazionali e tutta quell’algebra relazionale diventino parte del carico che la grammatica deve sostenere.
Dal punto di vista del supply chain, poiché i dati relazionali sono estremamente diffusi nel software enterprise, ha molto senso che una grammatica si occupi direttamente di tutte le questioni relazionali a livello di linguaggio.
Le grammatiche in informatica sono un argomento studiato in maniera enormemente approfondita. Esistono da decenni, eppure, è probabilmente l’unico ambito in cui i fornitori di software enterprise falliscono in modo più drastico. Infatti, finiscono invariabilmente per creare linguaggi di programmazione accidentali che emergono naturalmente ogni volta che sono in gioco impostazioni di configurazione complesse. Quando ci sono condizioni, trigger, cicli e risposte, solitamente è necessario gestire questo linguaggio invece di lasciarlo emergere spontaneamente.
Accade che quando non hai una grammatica, ogni volta che apporti modifiche all’applicazione, finirai per avere conseguenze casuali sul comportamento effettivo del sistema. A proposito, ciò spiega anche perché l’aggiornamento da una versione di un software enterprise a un’altra è solitamente molto complesso. La configurazione dovrebbe essere la stessa, ma quando effettivamente provi a eseguire la stessa configurazione nella versione successiva del software, ottieni risultati completamente differenti. La causa principale di questi problemi è la mancanza di una grammatica e di semantiche formalizzate stabilite per ciò che la configurazione intende significare.
Il modo tipico per rappresentare una grammatica è formalmente utilizzando la Backus-Naur Form (BNF), che è una notazione speciale. Sullo schermo, quello che puoi vedere è un mini linguaggio di programmazione che rappresenta gli indirizzi postali degli Stati Uniti. Ogni riga con un segno di uguale rappresenta una regola di produzione. Quello che hai a sinistra è un simbolo non terminale, e a destra del segno di uguale c’è una sequenza di simboli terminali e non terminali. I simboli terminali sono in rosso e rappresentano simboli che non possono essere derivati ulteriormente. I simboli non terminali sono racchiusi tra parentesi e possono essere ulteriormente derivati. Questa grammatica qui non è completa; ci sarebbero molte più regole di produzione da aggiungere per avere una grammatica completa. Volevo solo mantenere questa diapositiva ragionevolmente concisa.
Una grammatica è qualcosa di molto semplice da definire in termini di sintassi per il tuo linguaggio di programmazione, e garantisce anche che essa non sia ambigua. Tuttavia, non è perché sia scritta in Backus-Naur Form che sarà una grammatica valida o addirittura una buona grammatica. Per avere una buona grammatica, dobbiamo fare qualcosa in più. Il modo matematico di caratterizzare una buona grammatica è avere una grammatica context-free. Una grammatica si dice context-free se le regole di produzione possono essere applicate per qualsiasi simbolo non terminale, indipendentemente dai simboli che trovi a destra e a sinistra. L’idea è che una grammatica context-free è qualcosa in cui puoi applicare le regole di produzione in qualsiasi ordine, e non appena vedi una corrispondenza o derivazione, la applichi.
Ciò che ottieni con una grammatica context-free è una grammatica che, se cambi qualcosa al suo interno e questo cambiamento crea un’ambiguità, il compilatore non sarà in grado di compilare il programma in cui si verifica l’ambiguità. Questo è di primaria importanza quando intendi mantenere una configurazione per un lungo periodo di tempo. In supply chain, la maggior parte del software enterprise è molto duraturo. Non è raro vedere pezzi di software enterprise funzionare per due o tre decenni. Da Lokad, serviamo oltre 100+ aziende, e non è affatto insolito che estraiamo dati da sistemi che sono in funzione da oltre tre decenni, specialmente con grandi aziende.
Con una grammatica context-free, ottieni la garanzia che se c’è un cambiamento da apportare a questo linguaggio (e ricorda, quando dico “linguaggio”, posso intendere qualcosa di basilare come le impostazioni di configurazione), sarai in grado di identificare le ambiguità che emergono quando applichi questo cambiamento. Questo invece di avere quelle ambiguità che si verificano senza che tu ti renda conto di avere un problema, il che può portare a difficoltà durante l’aggiornamento da un sistema all’altro.
Ciò che accade quando le persone non sanno nulla di grammatiche è che scrivono manualmente un parser. Se non hai mai sentito parlare di una grammatica, un ingegnere del software scriverebbe un parser, che è un programma che crea in modo casuale una sorta di albero che rappresenta la versione analizzata del tuo programma. Il problema con ciò è che finisci per avere una semantica per il tuo programma incredibilmente specifica per quella versione del programma che possiedi. Quindi, se cambi questo programma, cambi la semantica, e otterrai risultati diversi, il che significa che puoi avere la stessa configurazione ma un comportamento diverso per il tuo supply chain.
Fortunatamente, nel 2004, ci fu una piccola rivoluzione introdotta da Brian Ford con un articolo intitolato “Parsing Expression Grammars: A Recognition-Based Syntactic Foundation.” Con questo lavoro, Ford ha fornito alla comunità un modo per formalizzare quel tipo di parser ad hoc accidentale che esiste nel campo. Ad esempio, queste grammatiche sono chiamate Parsing Expression Grammars (PEGs), e con le PEGs, puoi convertire quei parser empirici semi-accidentali in vere e proprie grammatiche formali di qualche tipo.
Python, ad esempio, non ha esattamente una grammatica context-free ma ha una PEG. Le PEGs vanno abbastanza bene se hai un ampio insieme di test automatizzati perché, in questo caso, puoi gestire la preservazione della semantica nel tempo. Infatti, con le PEGs, hai una formalizzazione della tua grammatica, quindi sei in una situazione migliore rispetto al non avere alcuna grammatica e soltanto un parser. Tuttavia, in termini di evoluzione della semantica, con una PEG non rileverai automaticamente che hai un cambiamento di semantica se modifichi la grammatica stessa. Pertanto, è necessario disporre di una suite estesa di test automatizzati sopra la tua PEG, che, tra l’altro, è esattamente ciò che la comunità Python possiede. Hanno un insieme molto robusto ed esteso di test automatizzati. Ora, da una prospettiva supply chain, credo che le grammatiche non solo siano importanti per farti rendere conto della loro importanza, ma costituiscano anche un banco di prova. Puoi effettivamente testare i fornitori di software enterprise quando discuti di un software con una complessità significativa. Dovresti chiedere al fornitore della grammatica che usano per la configurazione complessa. Se il fornitore risponde con “cos’è una grammatica?”, allora sai di essere nei guai, e la manutenzione sarà probabilmente lenta e costosa.
La programmazione è molto difficile, e le persone faranno molti errori. Se fosse facile, non avremmo nemmeno bisogno della programmazione. Un buon linguaggio di programmazione minimizza il tempo necessario per identificare un errore e correggerlo. Questo è uno degli aspetti più cruciali di un linguaggio di programmazione, garantendo una produttività decente per chiunque elabori codice.
Consideriamo la seguente situazione: mentre scrivi codice, se l’errore può essere rilevato mentre digiti, come un errore di battitura con una sottolineatura rossa in Microsoft Word, il ciclo di feedback per correggere l’errore può essere breve quanto 10 secondi, il che è ideale. Se l’errore può essere rilevato solo quando inizi a eseguire il programma, il ciclo di feedback durerà almeno 10 minuti. In supply chain, spesso abbiamo grandi set di dati da processare, e non possiamo aspettarci che il programma cominci a scorrere tutti i dati in pochi secondi. Di conseguenza, se il problema si verifica solo in fase di esecuzione, il ciclo di feedback sarà di 10 minuti o più.
Se l’errore può essere rilevato solo dopo il completamento dello script, ovvero se il programma ha un errore ma non va in crash, il ciclo di feedback richiederà circa 10 ore o più. Siamo passati da 10 secondi di feedback in tempo reale a 10 minuti se dobbiamo eseguire il programma e poi 10 ore se dobbiamo ispezionare i risultati numerici e gli indicatori di prestazione (KPI) prodotti dal programma.
C’è uno scenario ancora peggiore: se la piattaforma su cui operi non è strettamente deterministica, il che significa che con lo stesso input e gli stessi dati, può darti risultati differenti. Questo non è così strano come potrebbe sembrare, poiché in supply chain potremmo avere cose come simulazioni Monte Carlo in corso. Se c’è una qualche casualità nei risultati, potremmo avere qualcosa che fallisce solo una volta ogni tanto, e in questa situazione, il ciclo di feedback è tipicamente più lungo di 10 giorni. Quindi, siamo passati da 10 secondi a 10 giorni, e c’è in gioco una posta in gioco enorme nel restringere questo ciclo di feedback. L’analisi statica rappresenta un insieme di tecniche che possono essere applicate per rilevare problemi, errori o malfunzionamenti senza nemmeno eseguire il programma. Con l’analisi statica, l’idea è che non eseguirai nemmeno il programma, il che significa che puoi segnalare l’errore in tempo reale mentre le persone digitano, proprio come una sottolineatura rossa per errori di battitura in Microsoft Word. Più in generale, c’è un forte interesse nel trasformare ogni problema in modo che si sposti in una fase di feedback più precoce, trasformando problemi che impiegherebbero giorni per essere identificati in minuti o minuti in secondi, e così via.
Da una prospettiva supply chain, abbiamo visto in una delle lezioni precedenti che le supply chain possono aspettarsi molto caos. Non possiamo avere cicli di rilascio classici in cui aspetti che venga consegnata la prossima versione del tuo software. A volte ci sono eventi straordinari, come un cambiamento tariffario, una nave portacontainer bloccata in un canale o una pandemia. Queste situazioni di emergenza richiedono correzioni d’emergenza, e la quantità di analisi statica che puoi fare sul tuo linguaggio di programmazione definisce praticamente quanto caos si verificherà in produzione a causa di errori non catturati in tempo reale durante la digitazione del codice. Gli eventi straordinari possono sembrare rari, ma in pratica, le sorprese in supply chain sono abbastanza comuni.
Esistono prove matematiche che dimostrano che non è possibile rilevare tutti gli errori con un linguaggio di programmazione generale in una situazione generale. Ad esempio, non è nemmeno possibile dimostrare che il programma terminerà, il che significa che non è possibile garantire che quanto hai scritto non continui a funzionare indefinitamente.
Con l’analisi statica, di solito ottieni tre categorie: alcune parti del codice sono probabilmente buone, alcune parti sono probabilmente cattive, e per molte cose nel mezzo, semplicemente non lo sai. L’idea è che più spostiamo dalla categoria “non so” a “codice cattivo”, più sforzo sarà necessario in termini di design del linguaggio per convincere il compilatore che il tuo programma è valido. Quindi, dobbiamo trovare un equilibrio tra quanto sforzo vuoi investire per convincere il linguaggio di programmazione che il tuo codice è corretto e quante garanzie vuoi avere sul programma a tempo di compilazione, persino prima che il programma venga eseguito. Questo è una questione di produttività.
Ora, un rapido elenco degli errori tipici rilevati con l’analisi statica include errori di sintassi, come virgole o parentesi dimenticate. Alcuni linguaggi di programmazione non riescono nemmeno a rilevare errori di sintassi prima del runtime, come Bash, il linguaggio shell su Linux. L’analisi statica può anche rilevare errori di tipo, che si verificano quando hai il tipo incorretto o il numero sbagliato di argomenti per una funzione che stai chiamando.
Il codice irraggiungibile può essere rilevato anch’esso, il che significa che il codice è corretto, ma non verrà mai eseguito perché l’intero programma può operare senza mai raggiungere quella parte di codice. È come codice morto o una connessione logica dimenticata. Il codice irrilevante è un altro problema che può essere identificato, in cui il codice viene eseguito ma non ha alcun impatto sul risultato finale. È una variante del codice irraggiungibile.
Il codice eccessivamente oneroso può anche essere rilevato, il che si riferisce a quel codice che verrebbe eseguito, tranne che la quantità di risorse di calcolo necessaria supera di gran lunga ciò che puoi permetterti per il tuo programma. Un programma consuma risorse di calcolo come memoria, storage e CPU. Attraverso l’analisi statica, puoi dimostrare che un blocco di codice consuma molte più risorse di quante tu possa permetterti, considerando i tuoi vincoli come il completamento del calcolo entro un determinato periodo di tempo. Vorresti che ciò fallisse in fase di compilazione anziché eseguire il programma per un’ora e poi farlo fallire a causa di un timeout, il che porterebbe a una produttività molto bassa.
Tuttavia, c’è un’inversione di rotta quando si parla di analisi statica. Mentre digiti, stai trattando con un programma che è sempre invalido. Ad ogni battuta di tasto, stai trasformando un programma valido in uno invalido. Una soluzione di livello industriale per questa situazione si chiama Language Server Protocol. Questo strumento viene fornito con un linguaggio di programmazione ed è all’avanguardia per quanto riguarda il feedback in tempo reale sugli errori del codice che stai digitando.
Attraverso un Language Server Protocol, puoi accedere a funzionalità come “go to definition” quando clicchi su una variabile. Il Language Server Protocol è fondamentalmente stateful, ricordando l’ultima versione del tuo programma che era corretta, insieme alle annotazioni e semantiche disponibili. Preserva queste annotazioni e dettagli extra quando si tratta del tuo programma rotto, giusto perché hai premuto un tasto in più, e non è più un programma valido. È un cambiamento radicale in termini di produttività, e ogni volta che c’è un certo grado di urgenza, fa una grande differenza per scopi di supply chain.
Ora, entriamo nel sistema di tipi. Come prima approssimazione, un sistema di tipi è un insieme di regole che sfrutta la categorizzazione degli oggetti nel tuo programma, o la categorizzazione degli elementi che manipoli, per chiarire se certe interazioni sono ammesse o meno. Ad esempio, i tipi tipici includono stringhe, interi e numeri a virgola mobile, tutti tipi molto basilari. Definirà che aggiungere due interi insieme è valido, ma aggiungere una stringa e un intero non è valido, tranne che in JavaScript perché le semantiche sono diverse lì.
I sistemi di tipi, in generale, sono un problema di ricerca aperto e possono diventare incredibilmente astratti. Per fare un po’ di chiarezza, dobbiamo precisare che esistono due tipi di tipi, che sono frequentemente confusi. Prima, ci sono i tipi di valori, che esistono solo a runtime quando il programma è effettivamente in esecuzione. Ad esempio, in Python, se consideriamo una funzione che restituisce il primo elemento di un array di interi, allora il tipo del valore restituito da questa funzione sarà un intero. Da questa prospettiva, tutti i linguaggi di programmazione hanno i tipi – sono tutti tipizzati.
Secondo, ci sono i tipi delle variabili, che esistono solo a tempo di compilazione mentre il programma viene compilato e non è ancora in esecuzione. La sfida con i tipi delle variabili è estrarre quante più informazioni possibili su quelle variabili a tempo di compilazione. Se torniamo all’esempio precedente, in Python, potrebbe essere possibile o meno identificare il tipo del valore restituito dalla funzione, poiché Python non è interamente fortemente tipizzato a tempo di compilazione.
Da una prospettiva supply chain, stiamo cercando un sistema di tipi che supporti ciò che intendiamo fare a beneficio della supply chain. Vogliamo essere il più restrittivi possibile per catturare problemi e bug in anticipo, ma anche il più flessibili possibile per consentire tutte le operazioni che potrebbero essere di interesse. Ad esempio, consideriamo l’addizione di una data e di un intero. In un linguaggio di programmazione ordinario, probabilmente diresti che non è legittimo, ma da una prospettiva supply chain, se abbiamo una data e vogliamo aggiungere sette giorni, avrebbe senso scrivere “date + 7”. Esistono molte operazioni nella pianificazione supply chain che implicano lo spostamento delle date di un certo numero di giorni, quindi sarebbe utile avere un’algebra in cui è consentito eseguire un’addizione tra una data e un numero.
In termini di tipi, vogliamo permettere di sommare una data ad un’altra? Probabilmente no. Tuttavia, vogliamo permettere la sottrazione tra due date? Perché no? Se sottraiamo una data da un’altra che la precede, otteniamo il delta, che potrebbe essere espresso in giorni. Questo ha molto senso per i calcoli coinvolti nella pianificazione.
Continuando con l’argomento delle date, vi sono anche caratteristiche che potrebbero essere di interesse quando si considera cosa un sistema di tipi dovrebbe fare per noi in termini di problematiche supply chain. Ad esempio, che dire di restringere l’intervallo di tempo accettabile? Potremmo affermare che le date al di fuori di un arco di 20 anni nel passato e 20 anni nel futuro non siano valide. È probabile che se stiamo eseguendo un’operazione di pianificazione e, ad un certo punto del programma, manipoliamo una data che è oltre 20 anni nel futuro, le probabilità siano tali da far dedurre che non si tratti di un scenario di pianificazione valido per la maggior parte delle industrie. Nella maggior parte dei casi non pianificheresti operazioni quotidiane più di 20 anni in anticipo. Quindi, non possiamo solo utilizzare i tipi consueti, ma ridefinirli in modi che siano più restrittivi e più adeguati agli scopi supply chain.
Inoltre, c’è l’intero aspetto dell’incertezza. Nella gestione della supply chain, guardiamo sempre al futuro, ma purtroppo il futuro è sempre incerto. Il modo matematico per abbracciare l’incertezza è attraverso le variabili casuali. Avrebbe senso incorporare nel linguaggio le variabili casuali per rappresentare la domanda futura incerta, lead times, e i resi dei clienti, tra le altre cose.
Da Lokad, abbiamo progettato Envision, un linguaggio di programmazione dedicato all’ottimizzazione predittiva delle supply chains. Envision è una miscela di SQL, Python, ottimizzazione matematica, machine learning e capacità di big data, il tutto integrato come cittadino di prima classe all’interno del linguaggio stesso. Questo linguaggio è fornito con un Ambiente di Sviluppo Integrato (IDE) basato sul web, il che significa che puoi scrivere uno script dal web e disporre di tutte le moderne funzionalità di editing del codice. Questi script operano su un file system distribuito integrato che accompagna l’ambiente Lokad, così il livello dati è completamente integrato nel linguaggio di programmazione.
Gli script Envision vengono eseguiti su una flotta di macchine, progettate per sfruttare l’intera cloud. Quando lo script viene eseguito, si distribuisce su molte macchine per eseguire il calcolo più velocemente. Sullo schermo puoi vedere la pipeline del compilatore usata da Envision. Oggi non discuteremo di questo linguaggio di programmazione; parleremo solo della sua pipeline del compilatore, perché questo è l’argomento di interesse per la lezione di oggi.
Innanzitutto, iniziamo con un testo che contiene lo script Envision. Esso rappresenta un programma scritto da un esperto supply chain, non da un ingegnere del software, per affrontare una specifica sfida supply chain. Questa sfida potrebbe consistere nel decidere cosa produrre, cosa rifornire, cosa spostare, o se regolare un prezzo verso l’alto o verso il basso. Questi casi d’uso comportano decisioni su cosa produrre, rifornire, spostare, o se regolare i prezzi verso l’alto o verso il basso. Il testo dello script contiene le istruzioni, e l’idea è di processare questo script e ottenere il Lossless Syntax Tree (LST). L’LST è di interesse perché è una rappresentazione molto specifica che non scarta nessun carattere. Anche gli spazi bianchi non significativi vengono preservati. Lo scopo è garantire che eventuali riscritture automatiche del programma non alterino il codice esistente. Questo approccio evita situazioni in cui strumenti riorganizzino il codice, spostino le indentazioni o causino altre problematiche che rendono il codice difficile da riconoscere.
Un’operazione di refactoring di base, ad esempio, potrebbe comportare la rinominazione di una variabile e di tutte le sue occorrenze nel programma, senza intaccare altro. Dall’LST, passiamo all’Abstract Syntax Tree (AST), dove semplifichiamo l’albero. Le parentesi non sono necessarie in questa fase, poiché la struttura ad albero definisce le priorità di tutte le operazioni. Inoltre, eseguiamo una serie di operazioni di desugar per rimuovere qualsiasi sintassi fornita a beneficio del programmatore finale.
Proseguendo dall’AST all’Abstract Syntax Graph (ASG), appiattiamo l’albero. Questo processo comporta la scomposizione di istruzioni complesse con espressioni altamente nidificate in una sequenza di istruzioni elementari. Ad esempio, un’istruzione come “a = b + c + d” verrebbe suddivisa in due istruzioni, ciascuna con una sola addizione. Questo è esattamente ciò che accade durante la transizione dall’AST all’ASG.
Dall’ASG, passiamo all’Optimization Graph (OG), dove eseguiamo il type shaping e il broadcasting, in particolare in relazione all’algebra relazionale. Come menzionato in precedenza, Envision incorpora un’algebra relazionale nel linguaggio. Come suggerito numerose volte, Envision integra un’algebra relazionale, come nei database relazionali o SQL, come cittadino di prima classe. Esistono numerose operazioni relazionali, e verifichiamo che queste operazioni siano valide in base allo schema delle tabelle su cui operiamo nella transizione dall’ASG all’OG. L’Optimization Graph (OG) rappresenta l’ultimo stadio del front-end del compilatore e consiste in operazioni relazionali pure ed elementari applicate al programma, rappresentando piccole porzioni di logica. Proprio come in SQL, questi elementi sono di natura relazionale.
Il optimization graph viene definito “optimization” perché vi sono numerose trasformazioni che avvengono da OG a OG. Queste trasformazioni si verificano perché, nel trattare l’algebra relazionale, organizzare le operazioni in certi modi può far eseguire il programma molto più velocemente. Ad esempio, in SQL, se hai prima un filtro e poi un’operazione, o viceversa, è molto meglio filtrare prima i dati e poi applicare l’operazione. Ciò garantisce che le operazioni vengano applicate solo ai dati necessari, migliorando l’efficienza.
Da Lokad, l’ultimo stadio del compilatore front-end è l’High Intermediate Representation (HIR). L’HIR è un confine pulito, stabile e documentato tra il front-end e il back-end della pipeline del compilatore. A differenza dell’Optimization Graph (OG), che cambia costantemente a causa delle euristiche, l’HIR è stabile e fornisce un input coerente per il back-end del compilatore. Inoltre, l’HIR è serializzabile, ossia può essere facilmente trasformato in un pacchetto di byte da spostare da una macchina all’altra. Questa proprietà è essenziale per distribuire i calcoli su più macchine.
Dall’High Intermediate Representation, passiamo alle “funcs”. Le funcs sono valori che devono ancora essere valutati e rappresentano i blocchi atomici di calcolo all’interno di un’esecuzione distribuita. Ad esempio, quando si sommano due vettori giganteschi provenienti da una tabella con miliardi di righe, ci sarà una serie di funcs che rappresentano diverse sezioni di tali vettori. Ogni func è responsabile dell’addizione di una porzione dei due vettori ed è eseguita su una macchina. I grandi calcoli vengono suddivisi in molte funcs per distribuire il carico di lavoro su più CPU e più macchine, se il calcolo è sufficientemente grande da giustificare tale distribuzione. Le funcs sono denominate “lazy” perché non vengono inizialmente valutate; vengono valutate solo quando necessario. Numerosi calcoli possono avvenire prima che alcune funcs vengano effettivamente eseguite, e una volta calcolata una func, questa viene sostituita dal suo risultato.
All’interno della func troverai il low intermediate representation, che rappresenta la logica imperativa di basso livello eseguita al suo interno. Può, ad esempio, includere cicli e accessi a dizionari. Infine, questo low-level intermediate representation viene compilato in bytecode, il quale rappresenta l’obiettivo finale della nostra pipeline del compilatore. Da Lokad, miriamo al bytecode .NET, tecnicamente noto come MSIL.
Da una prospettiva supply chain, ciò che è veramente interessante è che attraverso questa pipeline del compilatore, per quanto complessa, stiamo riproducendo il grado di integrazione riscontrato in Microsoft Excel. Il linguaggio è integrato con il livello dati e con il livello UI/UX, permettendo agli utenti di vedere e interagire con gli output del programma, proprio come farebbero con un foglio di calcolo Excel. Tuttavia, a differenza di Excel, ci addentriamo in territori molto più interessanti per la gestione supply chain, abbracciando concetti relazionali come cittadini di prima classe, oltre all’ottimizzazione matematica e al machine learning.
Sia l’ottimizzazione matematica che il machine learning in questa pipeline attraversano l’intera pipeline, invece di limitarsi a chiamare una libreria che risiede da qualche parte. Avere il machine learning come cittadino di prima classe nella pipeline permette messaggi di errore più intelligibili, il che fa una grande differenza in termini di produttività per gli esperti supply chain.
Come argomento finale, i compilatori al giorno d’oggi quasi sempre puntano a una macchina virtuale, ma queste macchine virtuali, a loro volta, sono compilate verso un’altra macchina virtuale. Sullo schermo, i tipici livelli VM trovati in un ambiente server sono molto simili a quelli che abbiamo con uno script Envision. Ho appena presentato la pipeline del compilatore, ma fondamentalmente sarebbe praticamente lo stesso stack se parlassimo di uno script Python o di un foglio di calcolo Excel eseguito su un server. Quando si progetta un compilatore, si sceglie essenzialmente il livello in cui iniettare il codice. Più profondo è il livello, maggiori sono le problematiche tecniche da affrontare. Per scegliere il livello, ci sono una serie di questioni da considerare.
Innanzitutto, c’è la sicurezza. Come proteggi la tua memoria e cosa dovrebbe o non dovrebbe essere accessibile dal programma? Se hai un linguaggio di programmazione generico, le opzioni sono limitate. Potresti dover operare a livello del sistema operativo ospite, anche se anche quello potrebbe non essere molto sicuro. Esistono metodi per isolare l’ambiente (sandboxing), ma è molto complicato, quindi potresti dover addirittura scendere di livello.
Secondo, c’è la questione delle funzionalità a basso livello che ti interessano. Ad esempio, questo potrebbe essere importante se vuoi ottenere un’esecuzione più performante, riducendo la quantità di risorse di calcolo necessarie per completare il tuo programma. Puoi decidere di scendere sufficientemente in basso da poter gestire direttamente la memoria e i thread. Tuttavia, con questo potere arriva la responsabilità di gestire effettivamente la memoria e i thread.
Terzo, ci sono le funzionalità di convenienza come la garbage collection, lo stack trace, il debugger e il profiler. In genere, tutta l’instrumentation intorno al compilatore è più complessa del compilatore stesso. La quantità di funzionalità di convenienza di cui puoi beneficiare non va sottovalutata.
Quarto, ci sono le questioni relative all’allocazione delle risorse. Se stai operando con un foglio di calcolo Excel sul tuo desktop, Excel può consumare tutte le risorse di calcolo della tua workstation. Tuttavia, con Envision o SQL, hai più utenti da servire e devi decidere come allocare le risorse. Inoltre, con Envision, non si tratta solo di servire più utenti, ma anche più aziende, poiché Lokad è multi-tenant. Questo ha senso in supply chain perché il bisogno di risorse computazionali è molto intermittente per la maggior parte delle supply chains.
Tipicamente, hai bisogno di un’esplosione molto intensa di calcolo per, ad esempio, mezz’ora o anche un’ora, e poi niente per le successive 23 ore. Poi, il ciclo si ripete quotidianamente. Se allocassi risorse hardware di calcolo per un’azienda, quelle risorse rimarrebbero inutilizzate per il 90% del tempo, se non di più. Quindi, desideri poter distribuire il carico di lavoro su molte macchine e su molte aziende, potenzialmente aziende che operano in diversi fusi orari.
Infine, c’è la questione dell’ecosistema. L’idea è che, quando si sceglie un livello specifico e una VM specifica a cui puntare per il compilatore, sarà abbastanza conveniente integrare e interfacciare il compilatore con tutto ciò che mira alle stesse macchine virtuali. Questo solleva la questione dell’ecosistema: cosa puoi trovare allo stesso livello di quanto stai puntando, per non dover reinventare la ruota per ogni singolo dettaglio della tua intera stack? Questa è l’ultima, e importante, preoccupazione.
In conclusione, congratulazioni a quei pochi fortunati che sono arrivati fin qui in questa serie di lezioni supply chain. Questa è probabilmente una delle lezioni più tecniche finora. I compilatori sono, si potrebbe dire, una parte estremamente tecnica; tuttavia, la realtà delle moderne supply chains è che tutto viene mediato attraverso un linguaggio di programmazione. Non esiste più una supply chain grezza e osservabile direttamente. L’unico modo per osservare una supply chain è attraverso la mediazione di record elettronici prodotti da tutti i componenti del software aziendale che costituiscono il panorama applicativo. Pertanto, è necessario un linguaggio di programmazione e, di default, questo linguaggio risulta essere Excel.
Tuttavia, se vogliamo fare meglio di Excel, dobbiamo riflettere seriamente su cosa significhi “meglio” da una prospettiva supply chain e su cosa implichi in termini di linguaggi di programmazione. Se un’azienda non dispone della strategia o della cultura adeguata, nessuna tecnologia la salverà. Tuttavia, se strategia e cultura sono solide, allora gli strumenti contano davvero. Gli strumenti, compresi i linguaggi di programmazione, definiranno la tua capacità di eseguire, la produttività che puoi aspettarti dai tuoi esperti supply chain e le prestazioni che otterrai dalla tua supply chain nel tradurre la strategia macro nelle migliaia di decisioni quotidiane che essa deve prendere. Essere in grado di valutare l’adeguatezza degli strumenti, compresi i linguaggi di programmazione che intendi utilizzare per affrontare le sfide supply chain, è di primaria importanza. Se non sei in grado di valutare, allora si tratta semplicemente di un cargo cult completo.
La prossima lezione riguarderà l’ingegneria del software. Oggi abbiamo discusso degli strumenti; tuttavia, la prossima volta parleremo delle persone che usano gli strumenti e del tipo di lavoro di squadra necessario per svolgere bene il lavoro. La lezione si terrà lo stesso giorno della settimana, mercoledì, alle 15:00, ora di Parigi.
Ora, darò un’occhiata alle domande.
Domanda: Quando si seleziona il software per le supply chains, come possono le imprese che non sono esperte di tecnologia valutare se il compilatore e la programmazione sono appropriati per le loro necessità?
Beh, sono piuttosto sicuro che una tipica azienda che gestisce una tipica supply chain non abbia le qualifiche per ingegnerizzare un veicolo, eppure riesce ad acquistare camion che sono adeguati per la loro supply chain e per i requisiti di trasporto. Non è perché non sei un esperto e non sei in grado di ricostruire e reingegnerizzare un camion che non puoi avere un’opinione molto solida sul fatto che sia un buon camion per le tue esigenze di trasporto. Quindi, non sto dicendo che le aziende che non sono esperte di tecnologia dovrebbero compiere un incredibile balzo in avanti e improvvisamente diventare esperti nella progettazione di compilatori. Tuttavia, credo che in appena un’ora e mezza abbiamo coperto un bel po’ di terreno. Con altre 10 ore di un’introduzione più dettagliata e a ritmo più lento, impareresti tutto ciò che avresti mai bisogno di sapere in termini di progettazione del linguaggio per scopi di supply chain.
C’è una differenza tra essere un esperto ed essere così incredibilmente ignoranti da farti vendere un motorino fingendoti che sia un camion. Se dovessimo trasporre questo tipo di ignoranza, che ho osservato in termini di progettazione del software aziendale, all’industria automobilistica, la gente affermerebbe che un motorino è un semirimorchio e viceversa, e ce la farebbe.
Questa serie di lezioni riguarda le scienze ausiliarie, quindi non c’è l’intenzione che le persone che desiderano diventare praticanti della supply chain diventino esperti in questi ambiti. Tuttavia, avendo una conoscenza di base, puoi andare molto lontano nella valutazione. La maggior parte delle volte hai solo bisogno di sapere abbastanza per porre domande difficili. Se il fornitore ti dà una risposta insensata, non dà una buona impressione. Se non sai nemmeno quali domande tecniche porre, rischi di essere ingannato.
Il mio suggerimento è che non è necessario diventare incredibilmente esperti in tecnologia; basta avere una competenza tale da poter operare come un dilettante a livello base, capace di individuare falle e valutare se l’intero sistema crolla o se c’è sostanza reale dietro. Lo stesso vale per l’ottimizzazione matematica, il machine learning, le CPU e così via. L’idea è sapere abbastanza per distinguere tra qualcosa di fraudolento e qualcosa di legittimo.
Domanda: Hai affrontato direttamente il problema dei linguaggi di programmazione esistenti non progettati per supply chain?
Questa è una domanda molto buona. Ingegnerizzare un linguaggio di programmazione completamente nuovo può sembrare assurdo. Perché non optare semplicemente per qualcosa di già ben consolidato, come Python, e apportare le piccole modifiche di cui abbiamo bisogno? Sarebbe stata un’opzione. Il problema è che la questione principale non è tanto ciò che dobbiamo aggiungere a quei linguaggi, ma ciò che dobbiamo rimuovere.
La mia principale preoccupazione riguardo Python non è che non abbia un’algebra probabilistica o che non disponga di un’algebra relazionale integrata. La mia critica numero uno è che è un linguaggio di programmazione generico e completamente capace, e quindi espone chi scrive codice a ogni sorta di concetti, come la programmazione orientata agli oggetti per Python, che sono del tutto irrilevanti per la supply chain. Il problema non era tanto prendere un linguaggio e aggiungere qualcosa, ma prendere un linguaggio e cercare di rimuovere tonnellate di cose. Tuttavia, il problema è che non appena rimuovi qualcosa da un linguaggio di programmazione esistente, rompi tutto.
Ad esempio, la prima versione di Python è stata rilasciata nel 1990, quindi è un linguaggio di programmazione di 30 anni. La quantità di codice in uno stack popolare come Python è assolutamente gigantesca, e per una buona ragione. Non lo sto criticando; è uno stack molto solido, ma è anche enorme. Così alla fine, abbiamo valutato varie opzioni: prendere un linguaggio di programmazione, sottrarre tonnellate di cose fino a essere soddisfatti di ciò che abbiamo, oppure considerare che tutti quei linguaggi di programmazione possiedono un enorme bagaglio di eredità.
Abbiamo valutato quanto impegno fosse necessario per creare un linguaggio completamente nuovo, e alla fine è risultata molto favorevole la creazione di un nuovo linguaggio. L’ingegnerizzazione di un nuovo linguaggio di programmazione è un campo ben consolidato, quindi per quanto possa sembrare incredibile, non lo è. Esistono centinaia di libri che offrono ricette, ed ora è persino accessibile agli studenti di informatica. Ci sono perfino professori nei dipartimenti di informatica che danno ai loro studenti, in un semestre, il compito di creare un compilatore per un nuovo linguaggio di programmazione.
Alla fine, abbiamo deciso che le supply chains erano abbastanza grandi da giustificare uno sforzo dedicato. Sì, puoi sempre riciclare elementi che non sono stati progettati per le supply chains, ma le supply chains rappresentano un settore e un insieme di problemi di portata mondiale e massiccia. Quindi abbiamo pensato che, considerando la scala in esame, ha senso fare la cosa giusta e creare qualcosa direttamente per la supply chain anziché riciclare accidentalmente.
Domanda: Per l’ottimizzazione della supply chain, Envision è appropriato in quanto comprende SQL, Python, ecc. Tuttavia, per WMS, ERP, dove il flusso di processo è fondamentale piuttosto che l’ottimizzazione matematica, come si può valutare il suo compilatore e linguaggio di programmazione?
Questa è una domanda molto buona. Personalmente ho giocato con l’idea che ci siano attori in questo settore che hanno effettivamente ingegnerizzato dei linguaggi di programmazione propri, solo per il vantaggio di implementare qualcosa di completamente transazionale per natura, orientato al workflow. La supply chain, a mio avviso, riguarda fondamentalmente l’ottimizzazione predittiva. Tuttavia, il signor Nannani ha completamente ragione; cosa dire di tutta la parte gestionale, come ERP, WMS, ecc.?
Si scopre che ci sono molte aziende in questo campo che hanno creato il proprio linguaggio di programmazione. Ho menzionato SAP, che ha ABAP, progettato proprio per questo scopo. Purtroppo, a mio parere, ABAP non è invecchiato molto bene. Ci sono molte cose in ABAP che non hanno davvero senso nel XXI secolo. Si nota chiaramente che questo sistema è stato progettato nel ‘83, e se ne evince. Ad esempio, in Microsoft Dynamics, l’ERP ha un proprio linguaggio di programmazione. Dynamics AX ha il suo linguaggio di programmazione, e ci sono molti progetti ERP che, in larga misura, portano il proprio linguaggio di programmazione. Quindi, esiste.
Ora, quei linguaggi sono davvero l’apice di ciò che possiamo fare in termini di linguaggi di programmazione moderni e all’avanguardia nel 2021? Non credo, ed è anche questo il problema di cui stavo parlando: i fornitori di software aziendale continuano a reinventare i linguaggi di programmazione, ma di solito fanno un lavoro pessimo in tal senso. È solo un design ingegneristico improvvisato. Non si prendono nemmeno il tempo di leggere i numerosi libri disponibili sul mercato, e poi ci sono poveri ingegneri che si ritrovano con un groviglio di problemi.
Tornando alla tua domanda, ho pensato all’idea che Lokad potesse avventurarsi in quest’area e creare un linguaggio progettato non per l’ottimizzazione, ma per supportare il workflow. Tuttavia, a questo punto, la crescita di Lokad è così grande che non possiamo separarci e occuparci dei workflow. Sono assolutamente certo che questo sia esatto, e che emergeranno nuovi attori che faranno un ottimo lavoro per la parte gestionale del problema. Lokad si occupa solo della parte di ottimizzazione delle supply chains; esiste anche la parte gestionale.
Domanda: Python è attualmente considerato un linguaggio di programmazione standard. Ci sono delle evoluzioni in corso sul mercato?
Questa è una domanda molto buona. Vedi, quando la gente mi parla di “gli standard”, io sono stato abbastanza a lungo da vedere gli standard venire e andare. Non sono molto vecchio, ma quando ero al liceo lo standard era il C++. Negli anni ‘90, il C++ era lo standard. Perché farlo in un altro modo? Poi è arrivato Java, intorno all’anno 2000, e la combinazione di Java e XML era lo standard.
Si diceva addirittura che le università all’epoca si fossero trasformate in “scuole di Java.” Quella era letteralmente la parola d’ordine intorno all’anno 2000; la gente diceva, “Questa non è più un’università di informatica; è solo una scuola di Java.” Alcuni anni dopo, quando ho fondato Lokad, il linguaggio di programmazione per qualsiasi cosa relativa alla statistica era ancora R. Python era ancora molto marginale, e R dominava assolutamente il campo in termini di analisi statistica.
Con il progredire dei linguaggi di programmazione, il C++ ha perso importanza. Microsoft ha introdotto C# nel 2002 e la piattaforma .NET, che ha cannibalizzato una parte significativa dell’ecosistema C++. Una gran parte degli sviluppatori C++ nel mondo lavorava in Microsoft, un’azienda enormemente potente. Il punto che voglio sottolineare è che c’è stata una continua evoluzione, e ogni anno la gente considera questo come se ci fosse uno standard, ma tale standard cambia continuamente.
JavaScript esisteva da 20 anni, ma non era nulla di significativo. Poi, un libro pubblicato intorno al 2009 o al 2012 intitolato “JavaScript: The Good Parts” ha rivelato che JavaScript non era completamente folle. Potevi usare JavaScript per un progetto reale senza perdere la testa; dovevi solo attenerti alle parti positive. Improvvisamente, JavaScript è esploso, e la gente ha iniziato a usarlo sul lato server con un sistema chiamato Node.js.
Python è emerso in rilievo solo pochi anni fa, dopo che la comunità Python ha subito un arduo aggiornamento dalla versione 2.7 alla versione 3.x. Al termine di questo aggiornamento, l’interesse per Python è stato rinnovato. Tuttavia, ci sono molti pericoli in agguato per Python. Non è un linguaggio molto valido secondo gli standard del XXI secolo. È un linguaggio di 30 anni, e se ne vede l’età. Se desideri qualcosa di migliore in ogni dimensione tranne che nella maturità, potresti guardare a Julia. Julia è superiore in quasi ogni aspetto rispetto a Python per la data science, tranne che per la maturità, in cui Julia è ancora anni indietro.
Ci sono un sacco di evoluzioni in corso, ed è facile confondere lo stato dell’industria con qualcosa che sembra uno standard destinato a durare. Ad esempio, nell’ecosistema Apple c’era Objective-C, e poi Apple ha deciso di produrre Swift come sostituto, che ora sta rimpiazzando l’Objective-C. Il panorama dei linguaggi di programmazione è ancora in piena evoluzione, e se guardiamo all’ecosistema tra dieci anni, probabilmente ci sarà una notevole trasformazione. Python potrebbe non emergere come il linguaggio di programmazione dominante, dato che esistono molte opzioni concorrenti che offrono soluzioni migliori.
Domanda: Le aziende alimentari e le startup di e-commerce spesso pensano di poter vincere la battaglia con team di data science e linguaggi generici. Quale sarebbe il tuo principale argomento di vendita per farle rivedere questo approccio e farle capire che hanno bisogno di qualcosa di più specifico per il problema?
Come ho detto, questo è il problema con l’effetto Dunning-Kruger. Dai a un ingegnere del software un sistema di programmazione lineare a variabili intere e, una settimana dopo, quella persona penserà di essere improvvisamente diventata un’esperta di ottimizzazione discreta. Quindi, come vinco la battaglia? A dire il vero, di solito non le vinciamo. Quello che faccio è descrivere come si svilupperanno le catastrofi.
È semplice quando si usano blocchi tecnologici generici per creare prototipi fantastici. Questi prototipi funzionano in modo brillante grazie all’illusione alla Star Wars – hai solo il tuo pezzo di tecnologia isolato. Quando queste aziende inizieranno a cercare di portare quei prototipi in produzione, incontreranno difficoltà, per lo più a causa di problemi molto banali. Affronteranno continue problematiche di integrazione, non come Google, Microsoft o Amazon, che possono permettersi di avere mille ingegneri per occuparsi di tutta l’infrastruttura.
TensorFlow, per esempio, è difficile da integrare. Google ha i 1000 ingegneri necessari per incollare TensorFlow in tutte le loro data pipelines e applicazioni per i loro scopi. Ma la domanda è: le startup o le aziende di e-commerce possono permettersi di avere così tante persone che si occupino di tutta l’infrastruttura? Di solito la risposta è no. La gente immagina che scegliendo semplicemente questi strumenti, sarà in grado di selezionare alcuni elementi e metterli insieme, e magicamente funzionerà. Ma non è così. Richiede un’enorme quantità di lavoro ingegneristico.
Tra l’altro, alcuni fornitori di software aziendale stanno soffrendo dello stesso problema. Hanno troppi componenti nella loro soluzione, e questo spiega perché l’implementazione di una soluzione, senza alcuna personalizzazione, richiede già mesi, perché ci sono così tante parti instabili del sistema che sono solo vagamente integrate. Diventa molto difficile.
Credo che questa sia stata l’ultima domanda. Ci vediamo alla prossima.