Sind kleine Mengen an funktionaler Programmierung für Nicht-FP-Mitarbeiter verständlich? [geschlossen]

43

Fall : Ich arbeite in einer Firma und schreibe eine Anwendung in Python, die viele Daten in Arrays verarbeitet. Ich bin der einzige Entwickler dieses Programms im Moment, aber es wird wahrscheinlich in Zukunft (1-3 Jahre) von einem anderen Programmierer verwendet / modifiziert / erweitert, der mir derzeit unbekannt ist. Ich werde wahrscheinlich nicht direkt da sein, um zu helfen, aber vielleicht per E-Mail Unterstützung geben, wenn ich Zeit dafür habe.

Als Entwickler, der funktionales Programmieren (Haskell) gelernt hat, neige ich dazu, beispielsweise folgende Filter zu lösen:

filtered = filter(lambda item: included(item.time, dur), measures)

Der Rest des Codes ist OO, es sind nur einige kleine Fälle, in denen ich ihn so lösen möchte, weil er meiner Meinung nach viel einfacher und schöner ist.

Frage : Ist es heute in Ordnung, Code wie diesen zu schreiben?

  • Wie reagiert ein Entwickler, der FP nicht geschrieben / gelernt hat, auf Code wie diesen?
  • Ist es lesbar
  • Modifizierbar?
  • Soll ich einem Kind in einer Dokumentation erklären, was die Zeile bewirkt?

     # Filter out the items from measures for which included(item.time, dur) != True

Ich habe meinen Chef gefragt, und er sagt nur: "FP ist schwarze Magie, aber wenn es funktioniert und die effizienteste Lösung ist, ist es in Ordnung, es zu verwenden."

Wie ist Ihre Meinung dazu? Wie reagieren Sie als Nicht-FP-Programmierer auf den Code? Ist der Code "googbar", damit Sie verstehen können, was er bewirkt? Ich würde mich über Feedback freuen.

kd35a
quelle
14
Meiner Meinung nach erlauben viele moderne Sprachen ein gewisses Maß an funktionaler Programmierung, und deren Verwendung wird immer häufiger. Persönlich würde ich sagen, mach mit (zumindest für die relativ einfachen Filter- / Mapping-Aufgaben).
Joachim Sauer
Ich kann Ihnen keine unberührte Antwort geben, weil ich FP mag. Es wäre eine gute Idee, einen Kommentar hinzuzufügen, wenn dies nicht einfach ist.
Bruno Schäpper
3
Ihr Kommentar könnte klarer sein; Der Filter enthält Elemente, für die der Test "Wahr" lautet. Daher könnte Ihr Kommentar lauten: # Select the item's from measures for which included(item.time, dur) == TrueWenn Sie ein doppeltes Negativ vermeiden, wird das Verständnis immer verbessert.
Martijn Pieters
6
Haben Sie darüber nachgedacht, stattdessen Listenverständnisse zu verwenden? Sie werden oft als "pythonischer" als Map / Filter angesehen.
Phant0m
2
@ MatjazMuhic Nun, das ist schön ...;)
kd35a

Antworten:

50

Ist es lesbar

Für mich: Ja, aber ich habe verstanden, dass die Python-Community Listenverständnisse oft als sauberere Lösung betrachtet als die Verwendung von map()/ filter().

Tatsächlich hat GvR sogar in Betracht gezogen , diese Funktionen ganz fallen zu lassen.

Bedenken Sie:

filtered = [item for item in measures if included(item.time, dur)]

Dies hat außerdem den Vorteil, dass ein Listenverständnis immer eine Liste zurückgibt. map()und filter()auf der anderen Seite wird ein Iterator in Python 3 zurückgegeben.

Hinweis: Wenn Sie stattdessen einen Iterator haben möchten, ist dies so einfach wie das Ersetzen []durch ():

filtered = (item for item in measures if included(item.time, dur))

Um ehrlich zu sein, sehe ich wenig map()oder keinen Grund, filter()Python zu verwenden.

Ist es änderbar?

Ja, natürlich gibt es jedoch eine Sache, die das einfacher macht: Machen Sie es zu einer Funktion, nicht zu einem Lambda.

def is_included(item):
    return included(item.time, dur)
filtered = filter(is_included, measures)
filtered = [item for item in measures if is_included(item)]

Wenn Ihre Bedingung komplexer wird, wird dies viel einfacher skalieren, und Sie können Ihren Scheck wiederverwenden. (Beachten Sie, dass Sie Funktionen in anderen Funktionen erstellen können. Dies kann dazu führen, dass sie näher an der Stelle sind, an der sie verwendet werden.)

Wie reagiert ein Entwickler, der FP nicht geschrieben / gelernt hat, auf Code wie diesen?

Er googelt nach der Python-Dokumentation und weiß, wie es fünf Minuten später funktioniert. Ansonsten sollte er nicht in Python programmieren.

map()und filter()sind extrem einfach. Es ist nicht so, dass Sie sie bitten, Monaden zu verstehen. Deshalb glaube ich nicht, dass Sie solche Kommentare schreiben müssen. Verwenden Sie gute Variablen- und Funktionsnamen, dann ist der Code fast selbsterklärend. Sie können nicht vorhersagen, welche Sprachfunktionen ein Entwickler nicht kennt. Nach allem, was Sie wissen, weiß der nächste Entwickler möglicherweise nicht, was ein Wörterbuch ist.

Was wir nicht verstehen, ist für uns normalerweise nicht lesbar. Sie können also behaupten, dass es nicht weniger lesbar ist als ein Listenverständnis, wenn Sie noch nie eines von beiden gesehen haben. Aber wie Joshua in seinem Kommentar erwähnt hat, halte ich es auch für wichtig, mit anderen Entwicklern übereinzustimmen - zumindest, wenn die Alternative keinen wesentlichen Vorteil bietet.

phant0m
quelle
5
Es könnte sich lohnen auch zu erwähnen sein Generator Ausdrücke , die die gleiche wie Listenkomprehensionen aber Rückkehr Generatoren statt Listen arbeiten, wie mapund filtertun in Python 3.
James
@ James: Tolle Idee, ich habe sie zu meinem Beitrag hinzugefügt. Vielen Dank!
Phant0m
2
Ich rufe normalerweise reduceals Gegenbeispiel zu dem auf, dass Sie immer ein Argument für das Listenverständnis verwenden können , da es relativ schwierig ist, es durch reduceeinen Inline-Generator oder ein Listenverständnis zu ersetzen .
Kojiro
4
+1, um das bevorzugte Idiom der fraglichen Sprache zu notieren. IMHO-Konsistenz hat einen enormen Wert und die Verwendung einer Sprache in der Art und Weise, wie es ein erheblicher Teil der "Sprecher" tut, kann zuweilen eine bessere Wartbarkeit als sogar Kommentare bieten.
Joshua Drake
4
+1 für "Er googelt nach der Python-Dokumentation und weiß, wie es fünf Minuten später funktioniert. Andernfalls sollte er nicht in Python programmieren."
Bjarke Freund-Hansen
25

Da sich die Entwicklergemeinschaft wieder für funktionale Programmierung interessiert, ist es nicht ungewöhnlich, dass einige funktionale Programmiersprachen ursprünglich vollständig objektorientiert waren. Ein gutes Beispiel ist C #, wo anonyme Typen und Lambda-Ausdrücke es ermöglichen, durch funktionale Programmierung viel kürzer und aussagekräftiger zu sein.

Das heißt, funktionale Programmierung ist komisch für Anfänger. Als ich zum Beispiel den Anfängern während eines Schulungskurses erklärte, wie sie ihren Code durch funktionale Programmierung in C # verbessern können, waren einige von ihnen nicht überzeugt, und einige sagten, dass der ursprüngliche Code für sie leichter zu verstehen sei. Das Beispiel, das ich ihnen gab, war das folgende:

Code vor dem Refactoring:

var categorizedProducts = new Dictionary<string, List<Product>>();

// Get only enabled products, filtering the disabled ones, and group them by categories.
foreach (var product in this.Data.Products)
{
    if (product.IsEnabled)
    {
        if (!categorizedProducts.ContainsKey(product.Category))
        {
            // The category is missing. Create one.
            categorizedProducts.Add(product.Category, new List<Product>());
        }

        categorizedProducts[product.Category].Add(product);
    }
}

// Walk through the categories.
foreach (var productsInCategory in categorizedProducts)
{
    var minimumPrice = double.MaxValue;
    var maximumPrice = double.MinValue;

    // Walk through the products in a category to search for the maximum and minimum prices.
    foreach (var product in productsInCategory.Value)
    {
        if (product.Price < minimumPrice)
        {
            minimumPrice = product.Price;
        }

        if (product.Price > maximumPrice)
        {
            maximumPrice = product.Price;
        }
    }

    yield return new PricesPerCategory(category: productsInCategory.Key, minimum: minimumPrice, maximum: maximumPrice);
}

Gleicher Code nach dem Refactoring mit FP:

return this.Data.Products
    .Where(product => product.IsEnabled)
    .GroupBy(product => product.Category)
    .Select(productsInCategory => new PricesPerCategory(
              category: productsInCategory.Key, 
              minimum:  productsInCategory.Value.Min(product => product.Price), 
              maximum:  productsInCategory.Value.Max(product => product.Price))
    );

Das lässt mich denken, dass:

  • Sie sollten sich keine Sorgen machen, wenn Sie wissen, dass der nächste Entwickler, der Ihren Code wartet, über ausreichende allgemeine Erfahrung und einige Kenntnisse der funktionalen Programmierung verfügt, aber:

  • Vermeiden Sie andernfalls die funktionale Programmierung, oder geben Sie einen ausführlichen Kommentar ab, in dem Sie gleichzeitig die Syntax, die Vorteile und die möglichen Einschränkungen Ihres Ansatzes im Vergleich zu einer nicht funktionalen Programmierung erläutern.

Arseni Mourzenko
quelle
8
+1 Wenn FP Ihren Code für niemanden lesbarer macht, machen Sie es falsch.
György Andrasek
7
Ich kann sehen, wie isoliert jemand, der FP noch nie zuvor gesehen hat, das erste Snippet besser als das letztere lesen kann. Aber was ist, wenn wir dies mit 100 multiplizieren? Lesen Sie die 100 kurzen prägnanten Sätze, die fast wie in Englisch sind und beschreiben, was im Vergleich zu Tausenden von Zeilen, in denen es darum geht, wie Code geschrieben wird. Das schiere Volumen an Code, egal wie vertraut, sollte so überwältigend sein, dass die Vertrautheit kein großer Gewinn mehr ist. Irgendwann muss es für jeden einfacher sein, sich zuerst mit FP-Elementen vertraut zu machen und dann eine Handvoll Zeilen zu lesen, anstatt Tausende Zeilen vertrauten Codes zu lesen.
Esailija
7
Was mich am meisten stört, ist, dass wir uns damit zufrieden geben zu glauben, dass es für Entwickler akzeptabel ist, keinen funktionalen Stil zu erlernen. Die Vorteile wurden vielfach bewiesen, das Paradigma wird von den gängigen Sprachen unterstützt. Wenn die Menschen nicht in der Lage sind, funktionale Techniken zu verstehen, sollten sie unterrichtet werden oder sich selbst unterrichten, auch wenn sie nicht beabsichtigen, sie anzuwenden. Ich hasse XSLT mit einer brennenden Leidenschaft, aber das hat mich nicht davon abgehalten, es als Profi zu lernen.
Sprague
2
@MainMa Dein Punkt ist völlig gültig, ich hätte genauer sein sollen. Was ich damit meine, ist, dass von einem Entwickler in einer bestimmten Sprache erwartet werden sollte, dass er die Funktionen dieser Sprachen effektiv nutzt. Zum Beispiel ist die Verwendung des funktionalen Stils in C # (mit der Programmierung von Filtern / Zuordnen / Reduzieren von Stilen oder Übergeben / Zurückgeben von Funktionen) nichts Neues. Daher denke ich, dass alle C # -Entwickler, die solche Anweisungen nicht lesen können, ihre aktualisieren sollten Fähigkeiten ... wahrscheinlich sehr schnell. Ich denke, jeder Entwickler sollte ein bisschen Haskell lernen, aber nur aus akademischen Gründen. Das ist etwas anderes.
Sprague
2
@Sprague: Ich verstehe deinen Standpunkt und stimme ihm zu. Es ist nur so, dass wir zum Beispiel kaum erwarten können, dass C # -Programmierer anfangen, Paradigmen von FP zu verwenden, wenn zu viele dieser Programmierer nicht einmal Generika verwenden. Solange es nicht so viele ungelernte Programmierer gibt, ist die Messlatte in unserem Beruf niedrig, zumal die meisten Personen ohne technischen Hintergrund überhaupt nicht verstehen, worum es bei der Entwicklung geht.
Arseni Mourzenko
20

Ich bin ein Nicht-FP-Programmierer und habe kürzlich den Code meines Kollegen in JavaScript geändert. Es gab eine HTTP-Anfrage mit einem Rückruf, der der von Ihnen enthaltenen Aussage sehr ähnlich sah. Ich muss sagen, dass ich einige Zeit (etwa eine halbe Stunde) gebraucht habe, um alles herauszufinden (der Code war insgesamt nicht sehr groß).

Es gab keine Kommentare, und ich glaube, es gab keine Notwendigkeit dafür. Ich habe meinen Kollegen nicht einmal gebeten, mir zu helfen, seinen Code zu verstehen.

Angesichts der Tatsache, dass ich ungefähr 1,5 Jahre arbeite, denke ich, dass die meisten Programmierer in der Lage sein werden, solchen Code zu verstehen und zu modifizieren, seit ich das getan habe.

Außerdem gibt es, wie Joachim Sauer in seinem Kommentar sagte, oft Stücke von FP in vielen Sprachen, wie z. B. C # (indexOf). So viele Nicht-FP-Programmierer beschäftigen sich ziemlich oft damit, und das von Ihnen enthaltene Code-Snippet ist weder schrecklich noch unverständlich.

superM
quelle
1
Vielen Dank für Ihren Kommentar! Es gab mir mehr Informationen über die andere Perspektive. Es ist leicht, zu Hause blind zu werden, und dann wissen Sie nicht, wie es aussah, als Sie FP nicht kannten :)
kd35a
1
@ kd35a, gern geschehen. Zum Glück kann ich hier objektiv sein))
superM
1
+1 für den Hinweis, dass viele Sprachen jetzt Elemente von FP enthalten.
Joshua Drake
LINQ ist das Hauptbeispiel für FP in C #
Konrad Morawski,
3

Ich würde definitiv ja sagen!

Es gibt viele Aspekte der funktionalen Programmierung, und die Verwendung von Funktionen höherer Ordnung, wie in Ihrem Beispiel, ist nur einer davon.

Zum Beispiel halte ich das Schreiben von reinen Funktionen für äußerst wichtig für jede Software, die in einer beliebigen Sprache geschrieben wurde (wobei mit "rein" keine Nebenwirkungen gemeint sind), weil:

  • Sie sind einfacher zu Unit-Test
  • Sie sind viel komponierbarer als nebenwirkende Funktionen
  • Sie sind leichter zu debuggen

Ich vermeide es auch oft, Werte und Variablen zu verändern - ein anderes Konzept, das ich von FP übernommen habe.

Beide Techniken funktionieren problemlos in Python und anderen Sprachen, die normalerweise nicht als funktionsfähig eingestuft werden. Sie werden oft sogar von der Sprache selbst (dh finalVariablen in Java) unterstützt. Auf diese Weise werden zukünftige Wartungsprogrammierer nicht vor einer enormen Hürde stehen, den Code zu verstehen.


quelle
2

Wir hatten die gleiche Diskussion über ein Unternehmen, für das ich im letzten Jahr gearbeitet habe.

Die Diskussion betraf "magischen Code" und ob er gefördert werden sollte oder nicht. Bei genauerem Hinsehen schienen die Leute sehr unterschiedliche Ansichten darüber zu haben, was eigentlich "magischer Code" war. Diejenigen, die die Diskussion zur Sprache brachten, schienen hauptsächlich zu bedeuten, dass Ausdrücke (in PHP), die funktionalen Stil verwendeten, "magischer Code" waren, während Entwickler, die aus anderen Sprachen stammten, die mehr FP-Stil in ihrem Code verwendeten, anscheinend eher magischen Code hielten wenn Sie Dateien über Dateinamen usw. dynamisch einbinden.

Wir sind nie zu einem guten Schluss gekommen, mehr als die Leute denken, dass Code, der ungewohnt aussieht, "magisch" oder schwer zu lesen ist. Ist es also eine gute Idee, Code zu vermeiden, der anderen Benutzern unbekannt erscheint? Ich denke, dass es davon abhängt, wo es verwendet wird. Ich würde es unterlassen, fp-artige Ausdrücke (dynamische Einbeziehung von Dateien usw.) in einer Hauptmethode (oder in wichtigen zentralen Teilen von Anwendungen) zu verwenden, bei denen Daten auf eine klare und einfach zu lesende, intuitive Weise getunnelt werden sollen. Andererseits glaube ich nicht, dass man Angst haben sollte, den Briefumschlag zu schieben, die anderen Entwickler werden wahrscheinlich schnell FP lernen, wenn sie mit FP-Code konfrontiert sind und möglicherweise über eine gute interne Ressource verfügen, die sie zu diesen Themen konsultieren können.

TL; DR: Vermeiden Sie auf hoher Ebene zentrale Teile der Anwendungen (die gelesen werden müssen, um einen Überblick über die Funktionalität der Anwendung zu erhalten). Ansonsten benutze es.

Christopher Käck
quelle
Ein guter Punkt, um die Verwendung in höherem Code zu vermeiden!
kd35a
Ich habe immer gedacht, dass es an Formalismus mangelt und dass es sehr viel Gelegenheit gibt, Programmiertechniken in verschiedenen Arten von Blöcken zu definieren (wie statische Methoden, öffentliche Methoden, Konstruktoren usw.). Gute Antwort ... es bringt mich dazu, einige zu überdenken dieser Gedanken.
Sprague
2

Die C ++ - Community hat kürzlich auch Lambdas bekommen, und ich glaube, sie haben ungefähr die gleiche Frage. Die Antwort ist jedoch möglicherweise nicht dieselbe. Das C ++ - Äquivalent wäre:

std::copy_if(measures.begin(), measures.end(), inserter(filter),
  [dur](Item i) { return included(i, dur) } );

Jetzt std::copyist nicht neu und die _ifVarianten sind auch nicht neu, aber der Lambda ist es. Dennoch ist es im Kontext ziemlich klar definiert: durwird erfasst und ist daher konstant, Item ivariiert in der Schleife und die einzelne returnAnweisung erledigt die ganze Arbeit.

Dies scheint für viele C ++ - Entwickler akzeptabel zu sein. Ich habe jedoch keine Meinungen zu Lambdas höherer Ordnung gesammelt, und ich würde eine viel geringere Akzeptanz erwarten.

MSalters
quelle
Interessant ist, dass es je nach Sprache unterschiedliche Antworten geben kann. Wahrscheinlich im Zusammenhang mit dem Post von @Christopher Käck darüber, dass PHP-Codierer mehr Probleme mit solchen Dingen hatten als Python-Codierer.
kd35a
0

Senden Sie einem Kollegen, der nicht so fließend Python spricht, einen Code-Snippet und fragen Sie ihn, ob er 5 Minuten lang den Code überprüfen kann, um zu sehen, ob er ihn versteht.

Wenn ja, sind Sie wahrscheinlich gut zu gehen. Wenn nicht, sollten Sie es klarer machen.

Könnte Ihr Kollege dumm sein und etwas nicht verstehen, das offensichtlich sein sollte? Ja, aber Sie sollten immer nach KISS programmieren.

Vielleicht ist Ihr Code effizienter / gut aussehender / eleganter als ein einfacher, idiotensicherer Ansatz? Dann müssen Sie sich fragen: Muss ich das tun? Wieder, wenn die Antwort nein ist, dann tu es nicht!

Wenn Sie nach all dem immer noch denken, dass Sie es auf die FP-Art und Weise brauchen und wollen, dann tun Sie es auf jeden Fall. Vertraue deinem Instinkt, sie sind besser für deine Bedürfnisse geeignet als die meisten Leute in irgendeinem Forum :)

Arnab Datta
quelle
Zögern
Sie nicht