Come dovrei programmare con un linguaggio di battitura dinamico anche se sulla documentazione non viene annotato alcun tipo?

6

Ho vissuto a lungo nel mondo del linguaggio tipizzato statico. Tutti i tipi sono stati esplicitamente indicati su ogni variabile e argomento. Ora devo usare una specie di linguaggio di battitura dinamico. (Smalltalk) Il problema è che non è stato annotato alcun tipo su alcun argomento di messaggio e nemmeno sulla documentazione. Quindi, come posso sapere che cosa dovrebbe essere passato a ciascun argomento? Perché la gente non ha annotato il tipo sugli argomenti del messaggio? Davvero non è necessario?

    
posta Eonil 12.06.2011 - 20:24
fonte

3 risposte

1

Bene, le informazioni sul tipo saranno lì, ma devi cercarlo. In primo luogo, Smalltalk è concepito per essere utilizzato nell'ambiente di sviluppo ST e visualizzato con uno dei tanti browser di origine, quindi non provare a capire il codice come si potrebbe stampare da un programma C. Utilizzando il browser di sistema in Squeak, possiamo navigare verso la classe String e osservare alcuni dei messaggi che puoi inviare alle stringhe, come questa:

asLowercase
"Answer a String made up from the receiver whose characters are all lowercase."

^ self copy asString translateToLowercase

Puoi vedere che parla del ricevitore , che è la cosa a cui mandi un messaggio. Dato che stiamo osservando la classe string, questo deve essere di tipo String. Parla anche di una risposta che è, se ti piace, il tipo di ritorno del messaggio, e ti dice che il suo tipo è anche una stringa.

Per i messaggi che accettano parametri, come questo:

asPluralBasedOn: aNumberOrCollection
"Append an 's' to this string based on whether aNumberOrCollection is 1 or of size 1."

^ (aNumberOrCollection = 1 or:
    [aNumberOrCollection isCollection and: [aNumberOrCollection size = 1]])
        ifTrue: [self]
        ifFalse: [self, 's']

Puoi vedere che al parametro è stato dato un nome che indica quali tipi possono essere usati - in questo caso un numero o una raccolta come un elenco o un dizionario.

Quindi, le informazioni sul tipo sono disponibili, ma non vengono controllate dal compilatore. Se commetti un errore di tipo, Smalltalk ti metterà nel debugger e ti permetterà di risolverlo. È quindi normale che i programmatori della ST non si preoccupino molto del tipo e correggono eventuali errori che si verificano quando il loro codice viene eseguito attraverso i suoi test unitari. Dovrai davvero utilizzare i test unitari durante la programmazione in Smalltalk e c'è un buon quadro per te.

    
risposta data 12.06.2011 - 20:46
fonte
4

Sembra che manchi il punto di digitazione dinamica.

Non è insolito che funzioni ecc aspettino particolari tipi di dati degli argomenti anche in linguaggi dinamici. La documentazione di questo è proprio questo: documentazione, non codice. Alcuni tipi di asserzione sono spesso inclusi nel codice, tuttavia, per garantire che i requisiti dei tipi di dati siano soddisfatti.

Tuttavia, un linguaggio comune nelle lingue dinamiche è "digitazione anatra" - basato sulla frase "se cova come un'anatra, è un'anatra". Ciò significa che, se il valore passato supporta tutte le operazioni necessarie, generalmente non ti interessa quale sia il tipo esatto.

Quindi, il lato negativo delle lingue digitate dinamicamente è la mancanza di controllo automatico del tipo statico. Si ottiene ancora un controllo di tipo in fase di esecuzione: l'interprete (o compilatore JIT) tiene traccia del tipo di ciascun valore / oggetto, ma se si desidera un rigoroso controllo dei tipi lo si fa manualmente e in fase di esecuzione. Il vantaggio è maggiore praticità e flessibilità - e mentre potresti aver bisogno di un po 'più di test unitari per assicurarti la correttezza, dovresti comunque eseguire alcuni test unitari.

Il sistema di tipi usato da Haskell è un'interessante variante - tipizzata staticamente, ma le funzioni sono "generiche" per impostazione predefinita (dando una sorta di digitazione anatra), ma con un sistema di classe che consente di controllare la genericità (si può dire " questo argomento deve essere di qualche tipo che pretende di ciarlare come un'anatra ").

Modifica

Il punto è che esiste una "via di mezzo", in cui è possibile consentire a qualsiasi tipo di essere passato come argomento che supporta tutte le operazioni necessarie per impostazione predefinita, ma può limitare ulteriormente l'intervallo di tipi consentiti quando è necessario . Le funzioni generiche di default in Haskell sono pensate per essere viste come (anche quelle analoghe) usando la digitazione anatra in linguaggi dinamici, ma con l'opzione (specificando i vincoli di tipografia) per limitare quella libertà e recuperare qualunque sia il livello di cui hai bisogno tipo di sicurezza.

Anche questo non vuol dire che Haskell sia unico. Immagino che le stesse idee si applichino ai linguaggi della famiglia ML, anche se non sono mai stato un esperto in queste, e non ho nemmeno giocato con ATM per un po ', quindi non ho garanzie. In C ++, di solito descrivo i modelli C ++ come un "sottoinsieme funzionale in metaprogrammazione funzionale". Un modello C ++ è abbastanza simile a una funzione Haskell, infatti è una funzione valutata in fase di compilazione che restituisce una classe o una funzione. Gli argomenti (typename) sono digitati a forma di anatra - non esistono ulteriori restrizioni dirette se non che sono tipi. Sfruttare al massimo la flessibilità è un punto fondamentale della regola SFINAE. I concetti sono stati proposti (ma ritirati) da C ++ 0x per fare un modello WRT di lavoro simile a quello che fanno i typeclass Haskell per le funzioni - per fornire una via di mezzo limitando quella flessibilità.

Le interfacce Java sono correlate alle interfacce Haskell, ma non proprio la stessa cosa. In questo contesto, Java non è lo stesso di Haskell perché le funzioni Java non sono generiche per impostazione predefinita, anche se Java ha avuto il supporto per funzioni e classi generiche per un po 'di tempo. Non conosco bene Java, quindi non so se c'è un mezzo per quella "via di mezzo" o no.

Nei commenti, "tipizzazione strutturale" è stata descritta come l'equivalente di programmazione funzionale della digitazione anatra, il che è vero, tranne che è più l'equivalente tipizzato in modo statico della digitazione anatra. Prendi, ad esempio, questo tipo di funzione Haskell ...

mylength :: [a] -> Integer

L'argomento di input può essere una qualsiasi lista. L'ovvia parte di "duck typing" è che può prendere liste di qualsiasi tipo, ma tutti questi tipi e molti altri potrebbero essere accettati da una funzione con il seguente vincolo di tipo ...

myfunc :: a -> Integer

L'aspetto strutturale è che la firma mylength specifica che ogni argomento valido deve "ciarlare come un elenco". Questo differisce dal tipo di battitura a programmazione dinamica in cui il vincolo di "tipo di sottofondo" è esplicito, anche se ovviamente può essere dedotto.

Che cosa intendo per "ciarlatano come una lista"? Bene, l'elenco di Haskell viene sostanzialmente definito supportando le operazioni associate a un elenco. Ad esempio, una lista viene creata usando le operazioni ":" (spesso nascoste dietro lo zucchero a bretelle quadrate), quindi qualsiasi elenco può essere decostruito usando ":".

Non l'ho detto prima perché tutto ciò che significa veramente è che "è ciarlatano come un'anatra" viene controllato staticamente. Anche C lo fa fino ad un certo punto. Tuttavia, non fornisce (di per sé) quella via di mezzo che stavo cercando di descrivere. Anche se forse sostituendo il typedef C con il typedef D o un sottotipo Ada (un tipo distinto piuttosto che un alias) puoi ottenere la stessa via di mezzo nelle lingue imperative.

    
risposta data 12.06.2011 - 20:47
fonte
1

Le informazioni sul tipo sono ovviamente necessarie. L'intero punto di scrittura dei programmi sta manipolando le strutture di dati. Perché alcune persone pensano che buttare via le informazioni cruciali è una buona idea va oltre.

Dai un'occhiata a queste firme:

  1. Tipizzazione statica:

    public List<Invoice> GetInvoices(Customer c, Date d1, Date d2)
    
  2. Digitazione povera (detta anche dinamica):

    public GetInvoices(c, d1, d2)
    

In (1) c'è chiarezza. Sai esattamente con quali parametri hai bisogno di chiamare la funzione ed è chiaro cosa restituisce la funzione.

In (2) c'è solo incertezza. Non hai idea di quali parametri utilizzare e non sai cosa restituisce la funzione se qualcosa. Sei effettivamente costretto a utilizzare un approccio di prova ed errore inefficiente alla programmazione. Non raggiungi mai il punto in cui sai cosa stai facendo perché le informazioni cruciali sono intenzionalmente nascoste. Tutto ciò nel nome della semplicità e della velocità di sviluppo. Per aggiungere la beffa al danno, questo è preferito da alcune persone per alcuni strani motivi e chiamato "moderno".

Immagina di mantenere un progetto con centinaia di migliaia o addirittura milioni di righe di codice scritte in questo "stile" da più programmatori ...

Non so perché è chiamato "dinamico". È come scrivere un programma Java in cui ogni parametro, valore di ritorno e variabile locale possono essere solo di tipo oggetto. Le dichiarazioni di tipo sarebbero quindi ridondanti. Potresti quindi inventare un metodo statico per chiamare un metodo "dinamico":

public class DynamicMethodInvoker
{
    // second parameter should be a string, but we are restricted to a single type
    public static object Invoke(object thing, object method, object[] parameters)
    {
        // do some reflection stuff here
    }
}

Lettura suggerita: Le lingue dinamiche sono lingue statiche

    
risposta data 03.08.2011 - 16:17
fonte

Leggi altre domande sui tag