Il sistema di tipi di Haskell è formalmente equivalente a quello di Java? [chiuso]


66

Mi rendo conto che alcune cose sono più facili / difficili in una lingua rispetto all'altra, ma mi interessano solo le funzionalità relative al tipo che sono possibili in una e impossibili / irrilevanti nell'altra. Per renderlo più specifico, ignoriamo le estensioni di tipo Haskell poiché ce ne sono così tante là fuori che fanno tutti i tipi di cose folli / interessanti.


4
Anch'io sono curioso di sentire i teorici di categoria dalla lunga liquidazione che rispondono a questa domanda; anche se dubito che lo capirò particolarmente, sono ancora interessato a un dettaglio di questo. La mia inclinazione rispetto alle cose che ho letto è che il sistema di tipi HM consente al compilatore di sapere un sacco di cose su ciò che fa il tuo codice ed è per questo che è in grado di inferire i tipi così tanto da fornire così tante garanzie sul comportamento. Ma questo è solo il mio istinto e sono sicuro che ci sono altre cose di cui non sono assolutamente a conoscenza.
Jimmy Hoffa,

1
Questa è un'ottima domanda: è tempo di twittare ai follower per il grande dibattito su Haskell / JVM!
Martijn Verburg,

6
@ m3th0dman: Scala ha lo stesso supporto per le funzioni di ordine superiore di Java. In Scala, le funzioni di prima classe sono semplicemente rappresentate come istanze di classi astratte con un singolo metodo astratto, proprio come Java. Certo, Scala ha uno zucchero sintattico per la definizione di queste funzioni e ha una ricca libreria standard di tipi e metodi di funzioni predefiniti che accettano funzioni, ma dal punto di vista del sistema dei tipi , che è di cosa tratta questa domanda, non c'è differenza . Quindi, se Scala può farlo, anche Java può, e in effetti ci sono librerie FP ispirate a Haskell per Java.
Jörg W Mittag,

2
@ m3th0dman: Non è questa la domanda.
Phil

7
@ m3th0dman Sono tipi perfettamente ordinari. Non c'è niente di speciale nelle liste tranne alcune delizie sintattiche. È possibile definire facilmente il proprio tipo di dati algebrico che equivale al tipo di elenco incorporato ad eccezione della sintassi letterale e dei nomi dei costruttori.
sepp2k,

Risposte:


63

("Java", come usato qui, è definito come standard Java SE 7 ; "Haskell", come usato qui, è definito come standard Haskell 2010. )

Cose che ha il sistema di tipi Java ma che Haskell no:

  • polimorfismo del sottotipo nominale
  • informazioni sul tipo di runtime parziale

Cose che ha il sistema di tipi di Haskell ma che Java non lo fa:

  • polimorfismo ad hoc limitato
    • dà origine al polimorfismo del sottotipo "basato sui vincoli"
  • polimorfismo parametrico di tipo superiore
  • digitazione principale

MODIFICARE:

Esempi di ciascuno dei punti sopra elencati:

Unico a Java (rispetto a Haskell)

Polimorfismo del sottotipo nominale

/* declare explicit subtypes (limited multiple inheritance is allowed) */
abstract class MyList extends AbstractList<String> implements RandomAccess {

    /* specify a type's additional initialization requirements */
    public MyList(elem1: String) {
        super() /* explicit call to a supertype's implementation */
        this.add(elem1) /* might be overridden in a subtype of this type */
    }

}

/* use a type as one of its supertypes (implicit upcasting) */
List<String> l = new ArrayList<>() /* some inference is available for generics */

Informazioni sul tipo di runtime parziale

/* find the outermost actual type of a value at runtime */
Class<?> c = l.getClass // will be 'java.util.ArrayList'

/* query the relationship between runtime and compile-time types */
Boolean b = l instanceOf MyList // will be 'false'

Unico a Haskell (rispetto a Java)

Polimorfismo ad hoc limitato

-- declare a parametrized bound
class A t where
  -- provide a function via this bound
  tInt :: t Int
  -- require other bounds within the functions provided by this bound
  mtInt :: Monad m => m (t Int)
  mtInt = return tInt -- define bound-provided functions via other bound-provided functions

-- fullfill a bound
instance A Maybe where
  tInt = Just 5
  mtInt = return Nothing -- override defaults

-- require exactly the bounds you need (ideally)
tString :: (Functor t, A t) => t String
tString = fmap show tInt -- use bounds that are implied by a concrete type (e.g., "Show Int")

Polimorfismo di sottotipo "basato su vincoli" (basato su polimorfismo ad hoc limitato)

-- declare that a bound implies other bounds (introduce a subbound)
class (A t, Applicative t) => B t where -- bounds don't have to provide functions

-- use multiple bounds (intersection types in the context, union types in the full type)
mtString :: (Monad m, B t) => m (t String)
mtString = return mtInt -- use a bound that is implied by another bound (implicit upcasting)

optString :: Maybe String
optString = join mtString -- full types are contravariant in their contexts

Polimorfismo parametrico di tipo superiore

-- parametrize types over type variables that are themselves parametrized
data OneOrTwoTs t x = OneVariableT (t x) | TwoFixedTs (t Int) (t String)

-- bounds can be higher-kinded, too
class MonadStrip s where
  -- use arbitrarily nested higher-kinded type variables
  strip :: (Monad m, MonadTrans t) => s t m a -> t m a -> m a

Digitazione principale

Questo è difficile da dare un esempio diretto di, ma significa che ogni espressione ha esattamente un tipo massimamente generale (chiamato il suo tipo principale ), che è considerato il tipo canonico di quell'espressione. In termini di polimorfismo del sottotipo "basato sui vincoli" (vedi sopra), il tipo principale di un'espressione è il sottotipo unico di ogni tipo possibile che tale espressione può essere usata come. La presenza della digitazione principale in Haskell (non estesa) è ciò che consente l'inferenza di tipo completa (ovvero l'inferenza di tipo riuscita per ogni espressione, senza che siano necessarie annotazioni di tipo). Le estensioni che interrompono la tipizzazione principale (di cui ce ne sono molte) interrompono anche la completezza dell'inferenza del tipo.


7
Non usare lcome variabile a lettera singola, è MOLTO difficile distinguerlo 1!
recursion.ninja,

5
Vale la pena notare che, mentre hai assolutamente ragione sul fatto che Java abbia alcune informazioni sul tipo di runtime e Haskell no, puoi utilizzare la classe di tipo Typable in Haskell per fornire qualcosa che si comporta come informazioni sul tipo di runtime per molti tipi (con il più recente PolyKinded classi lungo la strada, saranno tutti i tipi iirc), anche se penso che dipenda dalla situazione se passa effettivamente qualsiasi informazione di tipo in fase di esecuzione o meno.

3
@DarkOtter ne sono consapevole Typeable, ma Haskell 2010 non ce l'ha (forse lo farà Haskell 2014?).
Ptharien's Flame,

5
Che dire dei tipi di somma (chiusi)? Sono uno dei meccanismi più importanti per i vincoli di codifica.
Tibet,

3
Nullability? Solidità (niente covarianza sillinses)? Tipi di somma chiusi / corrispondenze di modelli? Questa risposta è troppo stretta
Peaker il

32

Il sistema di tipo Java non ha un polimorfismo di tipo superiore; Il sistema di tipi di Haskell ce l'ha.

In altre parole: in Java, i costruttori di tipi possono astrarre sui tipi, ma non sui costruttori di tipi, mentre in Haskell i costruttori di tipi possono astrarre sui costruttori di tipi e sui tipi.

In inglese: in Java un generico non può prendere un altro tipo generico e parametrizzarlo,

public void <Foo> nonsense(Foo<Integer> i, Foo<String> j)

mentre in Haskell è abbastanza facile

higherKinded :: Functor f => f Int -> f String
higherKinded = fmap show

28
Ti dispiace farlo di nuovo da noi, questa volta in inglese? : P
Mason Wheeler,

8
@ Matt: A titolo di esempio non riesco a scrivere questo in Java, ma posso scrivere l'equivalente in Haskell: <T<_> extends Collection> T<Integer> convertStringsToInts(T<string> strings). L'idea qui sarebbe che se qualcuno lo chiamasse come convertStringsToInts<ArrayList>prenderebbe un arraylist di stringhe e restituirebbe un arraylist di numeri interi. E se invece lo usassero convertStringsToInts<LinkedList>, sarebbe lo stesso con gli elenchi collegati.
sepp2k,

8
Non è questo polimorfismo di tipo superiore, piuttosto che rango 1 vs n?
Adam,

8
@ JörgWMittag: La mia comprensione è che il polimorfismo di alto rango riguarda dove puoi inserire il foralltuo tipo. In Haskell, un tipo a -> bè implicitamente forall a. forall b. a -> b. Con un'estensione, puoi renderli forallespliciti e spostarli.
Tikhon Jelvis,

8
@Adam è rigoroso: il rango superiore e il tipo più elevato sono totalmente diversi. GHC può anche fare tipi di livello più alto (cioè tutti i tipi) con una certa estensione della lingua. Java non conosce tipi di classificazione superiore o di classificazione superiore.
Ingo,

11

A complemento delle altre risposte, il sistema di tipi di Haskell non ha sottotitoli , mentre i linguaggi orientati agli oggetti digitati come fanno Java.

Nella teoria del linguaggio di programmazione , il sottotipo (anche polimorfismo del sottotipo o polimorfismo dell'inclusione ) è una forma di polimorfismo di tipo in cui un sottotipo è un tipo di dati correlato a un altro tipo di dati (il supertipo ) da una nozione di sostituibilità , nel senso che elementi del programma, in genere subroutine o funzioni, scritte per operare su elementi del supertipo, possono operare anche su elementi del sottotipo. Se S è un sottotipo di T, la relazione del sottotipo è spesso scritta S <: T, per indicare che qualsiasi termine di tipo S può essere tranquillamente utilizzato in un contesto in cuiè previsto un termine di tipo T. La semantica precisa del sottotipo dipende fondamentalmente dai dettagli di ciò che "usato in sicurezza in un contesto in cui" significa in un determinato linguaggio di programmazione. Il sistema di tipi di un linguaggio di programmazione definisce essenzialmente la propria relazione di sottotipo, che può essere banale.

A causa della relazione di sottotipo, un termine può appartenere a più di un tipo. Il sottotipizzazione è quindi una forma di polimorfismo di tipo. Nella programmazione orientata agli oggetti il ​​termine "polimorfismo" è comunemente usato per riferirsi esclusivamente a questo polimorfismo di sottotipo , mentre le tecniche di polimorfismo parametrico sarebbero considerate programmazione generica ...


4
Ciò non significa che non si ottenga polimorfismo ad hoc. Si, solo in una forma diversa (digitare le classi invece del polimorfismo dei sottotipi).

3
La sottoclasse non è un sottotipo!
Frank Shearar,

8

Una cosa che nessuno ha menzionato finora è l'inferenza di tipo: un compilatore di Haskell di solito può inferire il tipo di espressioni ma devi dire al compilatore Java i tuoi tipi in dettaglio. In senso stretto, questa è una caratteristica del compilatore, ma la progettazione del linguaggio e del sistema di tipi determina se l'inferenza del tipo è fattibile. In particolare, l'inferenza del tipo interagisce male con il polimorfismo del sottotipo di Java e il sovraccarico ad hoc. Al contrario, i progettisti di Haskell si sforzano di non introdurre funzionalità che influiscono sull'inferenza del tipo.

Un'altra cosa che le persone non sembrano aver menzionato finora sono i tipi di dati algebrici. Cioè, la capacità di costruire tipi da somme ('o') e prodotti ('e') di altri tipi. Le classi Java fanno bene i prodotti (campo ae campo b, diciamo). Ma in realtà non fanno somme (campo a OR campo b, diciamo). Scala deve codificarlo come più classi di casi, il che non è esattamente lo stesso. E mentre funziona per Scala è un po 'complicato dire che Java ce l'ha.

Haskell può anche costruire tipi di funzione usando il costruttore di funzioni, ->. Sebbene i metodi Java abbiano firme di tipo, non è possibile combinarli.

Il sistema di tipi di Java abilita un tipo di modularità che Haskell non ha. Ci vorrà un po 'prima che ci sia un OSGi per Haskell.


1
@MattFenwick, ho modificato il terzo paragrafo in base al tuo feedback. I due sistemi di tipo trattano le funzioni in modo molto diverso.
GarethR

Non definirei ADT una caratteristica del sistema dei tipi. Puoi emularli completamente (se goffamente) con i wrapper OO.
circa il

@leftaroundabout Penso che sia discutibile. Ad esempio, potrebbero esserci delle cose che sono native di un sistema di tipi di una lingua, ma potrebbero essere implementate con modelli di progettazione in un'altra. Ovviamente, il modo di progettare il modello, rispetto a quello nativo, è una soluzione alternativa. Soluzione alternativa dovuta a un sistema di tipo più debole.
Ciao Angelo

La risposta scelta menzionava "inferenza di tipo completa" nella sezione "Digitazione principale". Java è in grado di emulare somme con sottotipi e informazioni sul tipo di runtime, ma come dici tu non è lo stesso della somma non è un attributo olistico del sistema di tipi.
Shelby Moore III,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.