Usa caso, disegno e come aggiungere un oggetto immutabile nella programmazione funzionale

0

Questa è non (dovrebbe essere) una domanda di opinione, ma è una domanda da principianti, quindi se c'è solo una risorsa che non ho trovato che ho bisogno di leggere, indicatemi lì :)

Sono in fase di progettazione di un sistema ETL basato su microservizi. Un servizio in particolare sembra essere un candidato naturale per la programmazione funzionale over oop, ma non volevo essere una soluzione alla ricerca di un problema.

Il servizio riceverebbe un oggetto dati complesso sotto forma di GraphFragment (penso json)

[
   {
      "type":"node",
      "label":"Product",
      "properties": {}
   },
   {
      "type":"node",
      "label":"Factor",
      "properties": {},
   },
   {
      "type":"Edge",
      "label":"INCLUDES_FACTOR",
      "properties": {},
      "from": 0, // Product
      "to": 1, // Factor
   },
]

Tranne molto più complesso con centinaia (forse migliaia, ma non di più) di nodi e spigoli, ognuno con molte proprietà.

Il microservizio, quindi, interroga il frammento del grafico e produce un report (anche in un oggetto simile a JSON). In quel rapporto ci sarebbero due diversi "tipi" di campi:

  1. Attributi (come un elenco di tutti i componenti di quel prodotto attraversando il grafico ad una profondità di n )
  2. Punteggi calcolati basati su una formula complessa che tiene conto della profondità di attraversamento, proprietà e pesi.

Il servizio in sé non ha la responsabilità di scrivere nulla fino alla fine, quando mette il prodotto finale su una coda kafka. Non dovrebbe richiedere nient'altro che il frammento del grafico.

Sembra una buona scelta per una pipeline con una collezione di funzioni che aggiungono ciascuna un attributo all'oggetto del report. Ogni funzione riceverebbe l'intero grafico e dovrebbe essere cieca rispetto all'output delle funzioni precedenti e future.

Alcuni altri requisiti sarebbero rapidi ed efficienti in termini di risorse. Questo processo specifico sarà usato molto più degli altri e dovrà essere il più performante possibile. Eseguire più funzioni in parallelo è interessante.

Quindi, suppongo di avere due domande:

  1. Questo è un buon uso per la programmazione funzionale, forse in Kotlin o Scala (o Python), che sono i tre linguaggi più utilizzati nell'applicazione nel suo complesso.

  2. Se le strutture dati sono immutabili, in che modo una funzione aggiunge un attributo al rapporto? È passato lo stato corrente del rapporto e quindi restituisce una copia del rapporto più il nuovo attributo.

Perché la memoria del suono mi spreca?

Apprezzo qualsiasi idea e sarei lieto di aggiungere chiarimenti.

    
posta Apollo 19.07.2017 - 21:08
fonte

3 risposte

0

Is this a good use case for functional programming, maybe in Kotlin or Scala (or Python), which are the three languages most used in the application as a whole.

Ingenuamente, sembra a posto. Camminare in uno stato di raccolta di grafici sembra decisamente funzionale (ancora meglio se si riesca a mappare / ridurre i nodi anziché camminare il grafico in serie).

If the data structures are immutable, how does one function add an attribute to the report? Is it handed the current state of the report and then returns a copy of that report plus the new attribute.

È possibile. Una soluzione migliore (se è effettivamente necessario raccogliere tutti i risultati prima di scriverli in coda) potrebbe restituire un ordinamento di elenco collegato di oggetti che è il nuovo attributo e un puntatore allo stato "precedente" del report. Al termine dell'elaborazione, restituisce l'ultimo attributo, che punta al precedente, che punta al precedente e così via.

    
risposta data 19.07.2017 - 21:48
fonte
0

If the data structures are immutable, how does one function add an attribute to the report? Is it handed the current state of the report and then returns a copy of that report plus the new attribute.

In una lingua funzionale, è possibile restituire una funzione che modifica il report e piegare il report sulle funzioni restituite (Scala):

type ReportModifier = Report => Report
type PipelineFunction = GraphFragment => ReportModifier

def executePipelineFunctionsInParallel(
    report: Report, 
    pipelineFunctions: List[PipelineFunction], 
    graphFragment: GraphFragment
): Future[Report] = {
    // Run our pipeline functions in parallel
    // Collect their results into Future[List[ReportModifier]]
    Future.traverse(pipelineFunctions)(pipelineFunction => Future {
        pipelineFunction(graphFragment)
    }).map(reportModifiers => reportModifiers.foldLeft(report) {
        case (report, reportModifier) => reportModifier(report)
    })
}
    
risposta data 19.07.2017 - 23:11
fonte
0

It seems like a good choice for a pipeline with a collection of functions that each add an attribute to the report object. Each function would receive the entire graph, and should be blind to the output of the previous and future functions.

Non aggiungere attributi uno alla volta. Utilizzare un'operazione simile alla mappa per creare il dizionario finale. Qualcosa di simile in Python:

# start with a dictionary mapping the attribute to the function
# that should produce that attribute
functions_to_apply = {
   'foobar': foobar_function,
   'goat': goat_function
}

# send the function and graph parameter to an executor who will run
# the operation in parallel and return a future.
with_futures = {key: executor.submit(value, graph) for key, value in functions_to_apply.items()}

# resolve all of the future to get your final object
final_report = {key: value.get() for key, value in with_futures.items()}
    
risposta data 19.07.2017 - 22:51
fonte