Modellazione di oggetti come funzioni

2

Voglio modellare oggetti semplici come funzioni, per vedere dove mi arriva il puro approccio funzionale. Diciamo che il mio oggetto è una persona. C'è qualche funzione che mi restituisce una nuova persona, ma invece di una semplice struttura dati, mi restituisce una funzione che rappresenta la persona. Ho difficoltà a vedere come dovrebbe essere esattamente questa funzione, perché in genere associo dati / stato a una persona, ma non a nessuna funzione. Quindi se chiamo questa persona, che è una funzione ora, quale input dovrebbe prendere e cosa dovrebbe restituire?

Se osservo il calcolo lambda e alcuni esempi su come modellare i numeri, 0 è solo la funzione di identità. 1 è una funzione che accetta un'altra funzione e un valore come input e restituisce la funzione argomento applicata al valore. 2 applica la funzione due volte, ecc.

Quindi l'implementazione di una persona dovrebbe essere una funzione che prende un'altra funzione come input e la applica alla persona, restituendone l'output? Quindi in realtà è solo un contenitore per i dati della persona che consente ad altre funzioni di operare su di esso.

Può essere che non abbia senso pensare a questo senza sapere esattamente in cosa consiste una persona. In un caso molto semplice una persona potrebbe essere solo un nome. Un breve tentativo di delineare questo in Clojure:

(defn new-person [firstname lastname] (fn [f] (f firstname lastname)))
(defn get-firstname [person] (person (fn [x y] x)))
(def p (new-person "Maja" "Abel"))
(get-firstname p)
"Maja"

Quindi di nuovo i nomi sono stringhe e non molto funzionali ...

Spero che questo abbia un senso, felice se qualcuno ha qualche idea su questo!

    
posta anselm 26.01.2014 - 16:35
fonte

1 risposta

6

Noi possiamo implementare i valori numerici come pure funzioni - ma in realtà non lo facciamo per motivi di prestazioni. Noi possiamo modellare una cella di controllo (e quindi liste arbitrarie) attraverso pure funzioni, ma di solito non lo facciamo. Ancora una volta, per motivi di prestazioni. E se possiamo modellare numeri e liste, possiamo modellare stringhe arbitrarie o altre cose.

Tuttavia, potrebbe essere utile pensare a questa cella / coppia per un momento.

Un costruttore di cons prende due elementi e restituisce qualcosa. La funzione car accede in qualche modo al primo elemento di questo qualcosa, mentre cdr accede alla seconda voce. Ecco un'implementazione in JavaScript:

function cons(x, y) {
  return function (gimme_first) {
    if (gimme_first) return x;
    return y;
  };
}

function car(cell) {
  return cell(true);
}

function cdr(cell) {
  return cell(false);
}

Quindi in sostanza ciò che abbiamo fatto è stato restituire una chiusura sullo stato. Possiamo accedere allo stato applicando la chiusura a un identificatore specifico.

Possiamo facilmente estenderlo a oggetti arbitrari: il nostro costruttore imposta alcuni contenuti dell'oggetto e restituisce una chiusura su di esso. Questa chiusura può essere applicata a un nome di campo per restituire un valore o un metodo (che si chiude anche sui dati dell'oggetto):

function Person(firstname, lastname) {
  // set up the methods as closure over our data
  // they could also take parameters if you'd like
  var get_firstname = function() { return firstname; };
  var get_lastname  = function() { return lastname;  };
  var get_fullname  = function() { return firstname + " " + lastname; };

  // set up the dispatcher
  return function(method) {
    if ("get-firstname" == method) return get_firstname;
    if ("get-lastname"  == method) return get_lastname;
    if ("get-fullname"  == method) return get_fullname;
    throw "Unknown method name " + method;
  }
}

Ora possiamo istanziare più oggetti:

var maja = Person("Maja", "Abel");
var freddy = Person("Freddy", "Smith");
maja("get-firstname")(); // Maja
freddy("get-lastname")(); // Smith

Come funziona l'ereditarietà? Il nostro costruttore si rimette a un altro costruttore e il nostro dispatcher passa il controllo al genitore:

function Employee(firstname, lastname, department) {
  // set up the parent object
  var parent = Person(firstname, lastname);
  // override a method
  var get_fullname = function() {
    return parent("get-fullname")() + " (from " + department + ")";
  };

  // set up the dispatcher:
  return function (method) {
    if ("get-fullname" == method) return get_fullname;
    return parent(method); // method lookup continues here
  };
}

Etc.

Questo è in realtà abbastanza simile al modo in cui JavaScript gestisce l'orientamento degli oggetti in ogni caso: il dispatcher di solito è un dizionario (chiamato comunque "oggetto") che è più efficiente delle nostre funzioni e lo stato dell'oggetto viene comunicato tramite il parametro implicito this di chiusure.

    
risposta data 26.01.2014 - 17:45
fonte

Leggi altre domande sui tag