2013-10-03 9 views
8

Sto imparando Ruby e sto avendo un grosso problema concettuale relativo alla digitazione. Consentitemi di spiegare perché non capisco con il paradigma.Come lavorare con Ruby Duck Typing

Dire che il metodo concatena per il codice conciso come si fa in Ruby. Devo sapere con precisione quale sia il tipo di ritorno di ciascun metodo chiamato nella catena, altrimenti non posso sapere quali metodi sono disponibili sul prossimo collegamento. Devo controllare la documentazione del metodo ogni volta ?? Mi sto imbattendo in questi esercizi tutorial costantemente in esecuzione. Sembra che io sia bloccato con un processo di riferimento, inferire, eseguire, fallire, correggere, ripetere per ottenere il codice in esecuzione piuttosto che sapere con precisione con cosa sto lavorando durante la codifica. Questo vola di fronte alla promessa di intuito di Ruby.

Dire che sto usando una libreria di terze parti, ancora una volta ho bisogno di sapere quali tipi sono autorizzati a passare i parametri altrimenti ho un fallimento. Posso guardare il codice ma possono o non possono esserci commenti o dichiarazioni di quale tipo il metodo si aspetta. Capisco che il codice basato sui metodi sia disponibile su un oggetto, non sul tipo. Ma poi devo essere sicuro che qualunque cosa io passi come parametro ha tutti i metodi che la libreria si aspetta, quindi devo ancora fare il controllo del tipo. Devo sperare e pregare che tutto sia documentato correttamente su un'interfaccia, quindi so se mi dovrei dare una stringa, un hash, una classe, ecc.

Se guardo la fonte di un metodo che posso ottenere un elenco di metodi che vengono chiamati e inferire il tipo previsto, ma devo eseguire analisi.

Ruby and duck typing: design by contract impossible?

Le discussioni in seno al precedente StackOverflow questione in realtà non rispondere altro che "non ci sono processi dovete seguire" e quei processi non sembrano essere standard, ognuno di noi ha un'opinione diversa su quale processo seguire e il linguaggio ha zero imposizione. Convalida del metodo? Design guidato dal test? API documentata? Convenzioni di denominazione del metodo rigoroso? Qual è lo standard e chi lo detta? Cosa seguo? Queste linee guida risolveranno questa preoccupazione https://stackoverflow.com/questions/616037/ruby-coding-style-guidelines? Ci sono editori che aiutano?

Concettualmente non ho nemmeno il vantaggio. Devi sapere quali sono i metodi necessari per qualsiasi metodo chiamato, quindi indipendentemente dal fatto che tu stia scrivendo quando codifichi qualcosa. Non stai semplicemente informando la lingua o chiunque altro esplicitamente, a meno che tu non decida di documentarlo. Quindi sei bloccato a eseguire tutti i controlli di tipo in fase di esecuzione anziché durante la codifica. Ho fatto programmazione in PHP e Python e non riesco nemmeno a capirlo.

Cosa mi manca o non capisco? Per favore aiutami a capire questo paradigma.

+2

Ci sono sicuramente dei vantaggi nell'annotare la digitazione, ma ovviamente ci sono anche degli svantaggi. Posso passare * qualsiasi * oggetto a un metodo 'foo (a)' che chiama 'a.bar' finchè' a.responds_to? (: Bar) '. Bello. Nessuna interfaccia, niente generici, basta aggiungere un metodo 'bar' e via. Naturalmente, la tipizzazione statica ha anche molti vantaggi. Sembra che tu abbia solo bisogno di un po 'di esperienza con il modo Ruby per apprezzare entrambi. Sembra anche che tu possa essere stato un po 'viziato da intellisense (o caratteristiche simili). Molti (* molti *) di noi devono controllare la documentazione in anticipo e spesso. –

+2

L'intuizione è il risultato dell'allenamento, quindi nulla è intuitivo finché non ti sei allenato a usarlo. –

+0

Questa domanda presenta "TL; DR". Sarebbe utile per te riassumere e buttare via il resto altrimenti è troppo ampio. "Questo vola di fronte alla promessa di intuito di Ruby." Intuitività a chi? Matz dice che è progettato per essere intuitivo per lui, e che crede che diventerà intuitivo per gli altri che lo hanno usato a sufficienza. È impossibile creare un linguaggio immediatamente intuitivo per tutti, quindi il suo obiettivo è azzeccato. –

risposta

1

Sì, sembri fraintendere il concetto. Non è un sostituto per il controllo di tipo statico. È solo diverso. Ad esempio, se si convertono oggetti in json (per renderli al client), non si cura del tipo effettivo dell'oggetto, purché abbia il metodo #to_json. In Java, dovresti creare l'interfaccia IJsonable. Nel rubino non è necessario un sovraccarico.

Per sapere cosa passare dove e cosa restituisce cosa: memorizza questo o consulta i documenti ogni volta. Lo facciamo tutti.

Solo un altro giorno, ho visto le rotaie programmatore 6+ anni di esperienza si lamentano su Twitter che non può memorizzare ordine dei parametri per alias_method: non nuovo nome va prima o l'ultima?

Questo vola di fronte alla promessa di intuito di Ruby.

Non proprio. Forse è solo una biblioteca scritta male. Nel core ruby ​​tutto è abbastanza intuitivo, oserei dire.

Le lingue tipizzate staticamente con i loro potenti IDE hanno un piccolo vantaggio qui, perché possono mostrare la documentazione proprio qui, molto rapidamente. Questo comunque sta accedendo alla documentazione, comunque. Solo più veloce.

+1

Penso che tu sia vicino a quello che sto ottenendo. Come dici tu dichiareresti IJsonable. In Ruby non lo dichiari. Allora come sai che hai bisogno di quell'interfaccia ?? Come dici tu DEVE consultare la documentazione, e la documentazione DEVE dichiarare che hai bisogno di quell'interfaccia, altrimenti non c'è modo di saperlo senza controllare il codice. Quindi qual è il vantaggio, devi dichiarare nei documenti, perché non dichiararlo in codice, quindi l'IDE conosce l'interfaccia e può effettivamente automatizzare le cose per te. Qual è anche il sovraccarico di dichiarare la tua interfaccia. – kamegami

+0

Il sovraccarico dell'interfaccia è un'altra entità nel sistema. Cos'è questa strana libreria con cui stai avendo problemi? Per la maggior parte di ciò con cui ho lavorato, accettano sia primitive (stringhe, matrici, hash, ecc.) (Che è ovvio dalla documentazione) che oggetti della stessa libreria (che sono, di nuovo, documentati). –

+0

Se si assegna un oggetto errato a un metodo, si genera un errore. Questo verrà rilevato dai test. –

4

Questo non è un problema specifico di Ruby, è lo stesso per tutte le lingue tipizzate dinamicamente.

Di solito non ci sono linee guida su come documentarlo (e il più delle volte non è realmente possibile). Si veda ad esempio map nella documentazione rubino

map { |item| block } → new_ary 
map → Enumerator 

cosa è item, block e new_ary qui e come sono collegate? Non c'è modo di dirlo a meno che tu non conosca l'implementazione o in qualche modo possa dedurlo dal nome della funzione. Specificare il tipo è anche difficile dal momento che new_ary dipende da ciò che restituisce block, che a sua volta dipende dal tipo di item, che potrebbe essere diverso per ciascun elemento nell'array.

Un sacco di volte si imbattono anche in documentazione che dice che un argomento è di tipo Object, che di nuovo non ti dice nulla poiché tutto è un oggetto.

OCaml ha una soluzione per questo, supporta digitando strutturale così una funzione che ha bisogno di un oggetto con una proprietà che è un fooString verrà dedotto essere { foo : String } invece di un tipo concreto. Ma OCaml è ancora tipizzato staticamente.

Vale la pena notare che questo può essere un problema anche nelle conversazioni statiche. Scala ha metodi molto generici sulle raccolte che portano a firme di tipo come ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[Array[T], B, That]): That per l'aggiunta di due raccolte.

Così la maggior parte del tempo, dovrete solo imparare questa memoria in lingue dinamicamente tipizzati, e, forse, contribuire a migliorare la documentazione delle librerie che si sta utilizzando.

Ed è per questo che preferisco tipizzazione statica;)

Modifica Una cosa che potrebbe avere un senso è quello di fare ciò che fa anche Scala. In realtà non vi mostrerà che la firma tipo per ++ di default, invece mostra ++[B](that: GenTraversableOnce[B]): Array[B] che non è così generica, ma probabilmente copre la maggior parte dei casi d'uso. Quindi per la mappa di Ruby potrebbe avere una firma di tipo monomorfo come Array<a> -> (a -> b) -> Array<b>. E 'corretta solo per i casi in cui l'elenco contiene solo valori di un tipo e il blocco restituisce solo gli elementi di un altro tipo, ma è molto più facile da capire e dà una buona panoramica di ciò che fa la funzione.

+0

Hai colpito il chiodo sulla testa, questo è il mio problema esatto. – kamegami

2

Considerare che le scelte di progettazione dei linguaggi fortemente tipizzati (C++, Java, C#, et al) applicano rigide dichiarazioni di tipo passate ai metodi e il tipo restituito dai metodi. Questo perché questi linguaggi sono stati progettati per convalidare che gli argomenti sono corretti (e dal momento che questi linguaggi sono compilati, questo lavoro può essere fatto in fase di compilazione). Ma alcune domande si può rispondere solo in fase di esecuzione, e C++, ad esempio ha la RTTI (Run Time Type Interpreter) esaminare e far rispettare tipo di garanzie. Ma come sviluppatore, sei guidato dalla sintassi, dalla semantica e dal compilatore per produrre codice che segua questi vincoli di tipo.

Ruby offre la flessibilità per utilizzare tipi di argomenti dinamici e restituire tipi dinamici. Questa libertà ti consente di scrivere codice più generico (leggi Stepanov su STL e programmazione generica) e ti offre un ricco set di metodi di introspezione (is_a ?, instance_of ?, response_to ?, kind_of ?, is_array ?, et al) che tu può usare dinamicamente. Ruby ti consente di scrivere metodi generici, ma puoi anche applicare esplicitamente la progettazione per contratto ed elaborare il fallimento del contratto con mezzi scelti.

Sì, sarà necessario prestare attenzione quando si concatenano i metodi insieme, ma l'apprendimento di Ruby non è solo un paio di nuove parole chiave. Ruby supporta molteplici paradigmi; puoi scrivere programmi procedurali, orientati agli oggetti, generici e funzionali. Il ciclo in cui ti trovi adesso migliorerà rapidamente man mano che impari su Ruby.

Forse la vostra preoccupazione deriva da un pregiudizio verso linguaggi fortemente tipizzati (C++, Java, C#, et al). La tipizzazione di anatra è un approccio diverso. Tu pensi diversamente. Digitare anatra significa che se un oggetto ha l'aspetto di a, si comporta come un, allora è un. Tutto (quasi) è un oggetto in Ruby, quindi tutto è polimorfico.

Considera modelli (il C++ li ha, C li ha, Java li riceve, C ha macro). Costruisci un algoritmo e poi il compilatore genera istanze per i tipi scelti. Non stai progettando per contratto con i generici, ma quando riconosci il loro potere, scrivi meno codice e produci di più.

Alcuni dei vostri altre preoccupazioni,

  • librerie di terze parti (gemme) non sono così difficile da usare come si teme
  • API documentate? Vedere rdoc e http://www.ruby-doc.org/
  • documentazione rdoc è (di solito) previsto per le librerie
  • linee guida di codifica - guardare il sorgente per un paio di gemme semplici per i principianti
  • convenzioni di denominazione - casi di serpente e di casi di cammello sono entrambi popolare

Suggerimento: avvicinarsi a un tutorial online con una mente aperta, fare il tutorial (http://rubymonk.com/learning/books/ è buono) e si avranno domande più mirate.

+0

Questa sembra essere la risposta che stavo cercando. Quindi dovrei eseguire l'introspezione come controlli di runtime appropriati sui parametri. E quindi DEVI generare documentazione al posto del tipo o delle dichiarazioni dell'interfaccia. La documentazione può essere generata dai controlli di introspezione? Mi sentirei molto più a mio agio nel dire che devi seguire la convenzione se ci fosse un PEP8 equivalente al punto e dire "questo è fatto male". – kamegami

+0

@kamegami: l'introspezione di Ruby non scoprirà i tipi previsti dal codice, quindi non può generare documentazione completa senza aiuto. Tuttavia, gli strumenti doc come 'yard' consentono ai codificatori di dichiarare i tipi per i parametri e i valori restituiti. Quindi ti affidi alle buone intenzioni degli sviluppatori della biblioteca con la documentazione. È possibile scrivere una documentazione molto accurata e accurata, se si ha tempo e inclinazione. –

+0

Sembra che se ci fosse una convenzione standard per dichiarare l'intento dei parametri (quello che chiamerò un oggetto con l'interfaccia che ci si aspetta) si potrebbe gestire la documentazione dell'interfaccia, la gestione degli errori e l'analisi dell'intento statico IDE (stile Intellisense) in un fallo piombare. NON POSSO essere l'unico a pensarlo per un linguaggio di tipo dinamico. – kamegami