Je me rends compte que certaines choses sont plus faciles/plus difficiles dans une langue que l'autre, mais je ne m'intéresse qu'aux fonctionnalités liées au type qui sont possibles dans l'une et impossibles/non pertinentes dans l'autre. Pour le rendre plus spécifique, ignorons les extensions de type Haskell car il y en a tellement qui font toutes sortes de choses folles/cool.
("Java", tel qu'utilisé ici, est défini comme standard Java SE 7 ; "Haskell", tel qu'utilisé ici, est défini comme Haskell standard 201 .)
Ce que possède le système de types de Java mais pas celui de Haskell:
Ce que possède le système de types de Haskell mais pas celui de Java:
MODIFIER:
Exemples de chacun des points énumérés ci-dessus:
/* 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 */
/* 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'
-- 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")
-- 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
-- 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
Celui-ci est difficile à donner un exemple direct, mais cela signifie que chaque expression a exactement un type au maximum général (appelé son type principal), qui est considéré comme le type canonique de cette expression. En termes de polymorphisme de sous-type "basé sur des contraintes" (voir ci-dessus), le type principal d'une expression est le sous-type unique de chaque type possible que cette expression peut être utilisée. La présence de la saisie principale dans Haskell (non étendu) est ce qui permet une inférence de type complète (c'est-à-dire une inférence de type réussie pour chaque expression, sans aucune annotation de type nécessaire). Les extensions qui rompent le typage principal (parmi lesquelles il y en a beaucoup) rompent également l'exhaustivité de l'inférence de type.
Le système de type de Java manque de polymorphisme de type supérieur; Le système de type de Haskell l'a.
En d'autres termes: en Java, les constructeurs de types peuvent abstraire sur les types, mais pas sur les constructeurs de types, tandis que dans Haskell, les constructeurs de types peuvent abstraire sur les constructeurs de types ainsi que sur les types.
En anglais: in Java un générique ne peut pas prendre un autre type générique et le paramétrer,
public void <Foo> nonsense(Foo<Integer> i, Foo<String> j)
à Haskell, c'est assez facile
higherKinded :: Functor f => f Int -> f String
higherKinded = fmap show
Pour compléter les autres réponses, le système de types de Haskell n'a pas sous-typage , tandis que les langages orientés objet sont comme Java font.
Dans théorie du langage de programmation , sous-typage (également polymorphisme de sous-type ou le polymorphisme d'inclusion ) est une forme de type polymorphisme dans laquelle un sous-type est un type de données qui est lié à un autre type de données (le supertype ) par certains notion de substituabilité , ce qui signifie que des éléments de programme, généralement des sous-programmes ou des fonctions, écrits pour fonctionner sur des éléments du supertype peuvent également fonctionner sur des éléments du sous-type. Si S est un sous-type de T, la relation de sous-typage est souvent écrite S <: T, pour signifier que tout terme de type S peut être utilisé en toute sécurité dans un contexte où un terme de type T est attendu. La sémantique précise du sous-typage dépend de manière cruciale des détails de ce que "utilisé en toute sécurité dans un contexte où" signifie dans un langage de programmation donné. Le type system d'un langage de programmation définit essentiellement sa propre relation de sous-typage, qui peut très bien être triviale.
En raison de la relation de sous-typage, un terme peut appartenir à plusieurs types. Le sous-typage est donc une forme de polymorphisme de type. Dans la programmation orientée objet, le terme "polymorphisme" est couramment utilisé pour désigner uniquement ce polymorphisme de sous-type, tandis que les techniques de polymorphisme paramétrique seraient considérées programmation générique ...
Une chose que personne n'a mentionnée jusqu'à présent est l'inférence de type: un compilateur Haskell peut généralement déduire le type d'expressions mais vous devez dire au compilateur Java votre type en détail. Strictement, c'est une caractéristique de la mais la conception du langage et du système de type détermine si l'inférence de type est possible. En particulier, l'inférence de type interagit mal avec le polymorphisme des sous-types de Java et la surcharge ad hoc. En revanche, les concepteurs de Haskell s'efforcent de ne pas introduire de fonctionnalités qui ont un impact sur l'inférence de type .
Une autre chose que les gens ne semblent pas avoir mentionnée jusqu'à présent est les types de données algébriques. C'est-à-dire la capacité de construire des types à partir de sommes ('ou') et de produits ('et') d'autres types. Java font très bien les produits (champ a et champ b, disons). Mais elles ne font pas vraiment de somme (champ a OR champ b, disons) . Scala doit coder cela en plusieurs classes de cas, ce qui n'est pas tout à fait la même chose. Et bien que cela fonctionne pour Scala c'est un peu exagéré de dire Java l'a.
Haskell peut également construire des types de fonction en utilisant le constructeur de fonction, ->. Bien que les méthodes de Java aient des signatures de type, vous ne pouvez pas les combiner.
Le système de types de Java permet un type de modularité que Haskell n'a pas. Il faudra un certain temps avant qu'il n'y ait un OSGi pour Haskell.