Was ist die Monomorphismusbeschränkung?

76

Ich bin verwirrt darüber, wie der Haskell-Compiler manchmal Typen ableitet, die weniger polymorph sind als erwartet, beispielsweise wenn punktfreie Definitionen verwendet werden.

Es scheint, dass das Problem die "Monomorphismus-Einschränkung" ist, die bei älteren Versionen des Compilers standardmäßig aktiviert ist.

Betrachten Sie das folgende Haskell-Programm:

{-# LANGUAGE MonomorphismRestriction #-}

import Data.List(sortBy)

plus = (+)
plus' x = (+ x)

sort = sortBy compare

main = do
  print $ plus' 1.0 2.0
  print $ plus 1.0 2.0
  print $ sort [3, 1, 2]

Wenn ich dies mit kompiliere ghc, erhalte ich keine Fehler und die Ausgabe der ausführbaren Datei ist:

3.0
3.0
[1,2,3]

Wenn ich den mainKörper ändere zu:

main = do
  print $ plus' 1.0 2.0
  print $ plus (1 :: Int) 2
  print $ sort [3, 1, 2]

Ich erhalte keine Fehler bei der Kompilierung und die Ausgabe wird:

3.0
3
[1,2,3]

wie erwartet. Wenn ich jedoch versuche, es zu ändern in:

main = do
  print $ plus' 1.0 2.0
  print $ plus (1 :: Int) 2
  print $ plus 1.0 2.0
  print $ sort [3, 1, 2]

Ich erhalte einen Tippfehler:

test.hs:13:16:
    No instance for (Fractional Int) arising from the literal ‘1.0’
    In the first argument of ‘plus’, namely ‘1.0’
    In the second argument of ‘($)’, namely ‘plus 1.0 2.0’
    In a stmt of a 'do' block: print $ plus 1.0 2.0

Das gleiche passiert, wenn Sie versuchen, sortzweimal mit verschiedenen Typen anzurufen :

main = do
  print $ plus' 1.0 2.0
  print $ plus 1.0 2.0
  print $ sort [3, 1, 2]
  print $ sort "cba"

erzeugt den folgenden Fehler:

test.hs:14:17:
    No instance for (Num Char) arising from the literal ‘3’
    In the expression: 3
    In the first argument of ‘sort’, namely ‘[3, 1, 2]’
    In the second argument of ‘($)’, namely ‘sort [3, 1, 2]’
  • Warum denkt man ghcplötzlich, dass plusdas nicht polymorph ist und ein IntArgument erfordert ? Der einzige Hinweis Intist in einer Anwendung von plus, wie kann das wichtig sein, wenn die Definition eindeutig polymorph ist?
  • Warum denkt ghcplötzlich, dass dies sorteine Num CharInstanz erfordert ?

Außerdem, wenn ich versuche, die Funktionsdefinitionen in einem eigenen Modul zu platzieren, wie in:

{-# LANGUAGE MonomorphismRestriction #-}

module TestMono where

import Data.List(sortBy)

plus = (+)
plus' x = (+ x)

sort = sortBy compare

Beim Kompilieren wird folgende Fehlermeldung angezeigt:

TestMono.hs:10:15:
    No instance for (Ord a0) arising from a use of ‘compare’
    The type variable ‘a0’ is ambiguous
    Relevant bindings include
      sort :: [a0] -> [a0] (bound at TestMono.hs:10:1)
    Note: there are several potential instances:
      instance Integral a => Ord (GHC.Real.Ratio a)
        -- Defined in ‘GHC.Real’
      instance Ord () -- Defined in ‘GHC.Classes’
      instance (Ord a, Ord b) => Ord (a, b) -- Defined in ‘GHC.Classes’
      ...plus 23 others
    In the first argument of ‘sortBy’, namely ‘compare’
    In the expression: sortBy compare
    In an equation for ‘sort’: sort = sortBy compare
  • Warum ist nicht der ghcLage , den polymorphen Typ verwenden Ord a => [a] -> [a]für sort?
  • Und warum ghcbehandelt plusund plus'anders? plussollte den polymorphen Typ haben Num a => a -> a -> aund ich sehe nicht wirklich, wie sich dieser vom Typ unterscheidet sortund dennoch nur sorteinen Fehler auslöst.

Letzte Sache: Wenn ich die Definition sortder Datei kommentiere, wird sie kompiliert. Wenn ich jedoch versuche, es zu laden ghciund die Typen zu überprüfen, die ich erhalte:

*TestMono> :t plus
plus :: Integer -> Integer -> Integer
*TestMono> :t plus'
plus' :: Num a => a -> a -> a

Warum ist der Typ nicht pluspolymorph?


Dies ist die kanonische Frage zur Monomorphismusbeschränkung in Haskell, wie in der Meta-Frage erörtert .

Bakuriu
quelle
Warum die plötzliche Ankündigung des öffentlichen Dienstes? Ich denke auch, dass "Ausschalten" in Ihrer Antwort wahrscheinlich stärker empfohlen werden sollte.
Feuer
2
@dfeuer Plötzlich? Die Meta-Frage wurde vor 4 Monaten gestellt . Ich habe vor 2 Wochen einen Entwurf der folgenden Antwort gepostet . Ich habe diese beiden Tatsachen auch vor einiger Zeit im Chat erwähnt. Für mich ist das nicht "plötzlich". Morgen werde ich sehen, was ich tun kann, um die wichtigsten Informationen hervorzuheben.
Bakuriu
1
Ah, ich habe die Meta-Referenz verpasst.
Feuer

Antworten:

101

Was ist die Monomorphismusbeschränkung?

Die im Haskell-Wiki angegebene Monomorphismus-Einschränkung lautet:

eine kontraintuitive Regel in der Haskell-Typinferenz. Wenn Sie vergessen, eine Typensignatur anzugeben, füllt diese Regel manchmal die freien Typvariablen mit bestimmten Typen unter Verwendung von "Typstandard" -Regeln.

Dies bedeutet, dass der Compiler unter bestimmten Umständen , wenn Ihr Typ mehrdeutig (dh polymorph) ist , diesen Typ auf etwas nicht mehrdeutiges instanziiert .

Wie behebe ich das?

Zunächst können Sie immer explizit eine Typensignatur angeben, um das Auslösen der Einschränkung zu vermeiden:

plus :: Num a => a -> a -> a
plus = (+)    -- Okay!

-- Runs as:
Prelude> plus 1.0 1
2.0

Wenn Sie eine Funktion definieren, können Sie alternativ einen punktfreien Stil vermeiden und beispielsweise schreiben:

plus x y = x + y

Ausschalten

Sie können die Einschränkung einfach deaktivieren, damit Sie nichts an Ihrem Code ändern müssen, um sie zu beheben. Das Verhalten wird durch zwei Erweiterungen gesteuert: MonomorphismRestrictionAktiviert es (dies ist die Standardeinstellung) und NoMonomorphismRestrictiondeaktiviert es.

Sie können die folgende Zeile ganz oben in Ihre Datei einfügen:

{-# LANGUAGE NoMonomorphismRestriction #-}

Wenn Sie GHCi verwenden, können Sie die Erweiterung mit dem folgenden :setBefehl aktivieren :

Prelude> :set -XNoMonomorphismRestriction

Sie können ghcdie Erweiterung auch über die Befehlszeile aktivieren:

ghc ... -XNoMonomorphismRestriction

Hinweis: Sie sollten die erste Option der Auswahl der Erweiterung über Befehlszeilenoptionen vorziehen.

Auf der Seite von GHC finden Sie eine Erläuterung dieser und anderer Erweiterungen.

Eine vollständige Erklärung

Ich werde versuchen, im Folgenden alles zusammenzufassen, was Sie wissen müssen, um zu verstehen, was die Monomorphismusbeschränkung ist, warum sie eingeführt wurde und wie sie sich verhält.

Ein Beispiel

Nehmen Sie die folgende triviale Definition:

plus = (+)

Sie würden denken, in der Lage zu sein, jedes Vorkommen von +durch zu ersetzen plus. Insbesondere , da (+) :: Num a => a -> a -> awürde man erwarten , dass auch haben plus :: Num a => a -> a -> a.

Dies ist leider nicht der Fall. Zum Beispiel versuchen wir in GHCi Folgendes:

Prelude> let plus = (+)
Prelude> plus 1.0 1

Wir erhalten folgende Ausgabe:

<interactive>:4:6:
    No instance for (Fractional Integer) arising from the literal ‘1.0’
    In the first argument of ‘plus’, namely ‘1.0’
    In the expression: plus 1.0 1
    In an equation for ‘it’: it = plus 1.0 1

Möglicherweise müssen Sie :set -XMonomorphismRestriction in neueren GHCi-Versionen.

Und tatsächlich können wir sehen, dass die Art von plusnicht das ist, was wir erwarten würden:

Prelude> :t plus
plus :: Integer -> Integer -> Integer

Was passiert ist, ist, dass der Compiler sah, dass plusTyp Num a => a -> a -> a, ein polymorpher Typ. Darüber hinaus fällt die obige Definition unter die Regeln, die ich später erläutern werde, und so hat er beschlossen, den Typ monomorph zu machen, indem er die Typvariable standardmäßig verwendet a. Die Standardeinstellung ist, Integerwie wir sehen können.

Beachten Sie, dass beim Versuch , den obigen Code mit zu kompilierenghc , keine Fehler auftreten. Dies liegt daran, wie die interaktiven Definitionen ghcibehandelt werden (und behandelt werden müssen ). Grundsätzlich kann jede Anweisung eingegeben in ghcimüssen vollständig geprüft Typ vor Folgendes berücksichtigt wird; Mit anderen Worten, es ist, als ob jede Anweisung in einem separaten Modul wäre . Später werde ich erklären, warum das so ist.

Ein anderes Beispiel

Betrachten Sie die folgenden Definitionen:

f1 x = show x

f2 = \x -> show x

f3 :: (Show a) => a -> String
f3 = \x -> show x

f4 = show

f5 :: (Show a) => a -> String
f5 = show

Wir würden erwarten, dass sich alle diese Funktionen gleich verhalten und den gleichen Typ haben, dh den Typ show: Show a => a -> String.

Beim Zusammenstellen der obigen Definitionen erhalten wir jedoch die folgenden Fehler:

test.hs:3:12:
    No instance for (Show a1) arising from a use of ‘show’
    The type variable ‘a1’ is ambiguous
    Relevant bindings include
      x :: a1 (bound at blah.hs:3:7)
      f2 :: a1 -> String (bound at blah.hs:3:1)
    Note: there are several potential instances:
      instance Show Double -- Defined in ‘GHC.Float’
      instance Show Float -- Defined in ‘GHC.Float’
      instance (Integral a, Show a) => Show (GHC.Real.Ratio a)
        -- Defined in ‘GHC.Real’
      ...plus 24 others
    In the expression: show x
    In the expression: \ x -> show x
    In an equation for ‘f2’: f2 = \ x -> show x

test.hs:8:6:
    No instance for (Show a0) arising from a use of ‘show’
    The type variable ‘a0’ is ambiguous
    Relevant bindings include f4 :: a0 -> String (bound at blah.hs:8:1)
    Note: there are several potential instances:
      instance Show Double -- Defined in ‘GHC.Float’
      instance Show Float -- Defined in ‘GHC.Float’
      instance (Integral a, Show a) => Show (GHC.Real.Ratio a)
        -- Defined in ‘GHC.Real’
      ...plus 24 others
    In the expression: show
    In an equation for ‘f4’: f4 = show

Also f2und f4nicht kompilieren. Darüber hinaus , wenn sie versuchen , diese Funktion in GHCi wir bekommen definieren keine Fehler , aber der Typ für f2und f4ist () -> String!

Monomorphie Einschränkung ist , was macht f2und f4einen monomorphen Typ erfordern, und das unterschiedliche Verhalten bewteen ghcund ghciist aufgrund unterschiedlicher säumige Regeln .

Wann passiert es?

In Haskell gibt es, wie im Bericht definiert , zwei verschiedene Arten von Bindungen . Funktionsbindungen und Musterbindungen. Eine Funktionsbindung ist nichts anderes als eine Definition einer Funktion:

f x = x + 1

Beachten Sie, dass ihre Syntax lautet:

<identifier> arg1 arg2 ... argn = expr

Modulo Wachen und whereErklärungen. Aber sie spielen keine Rolle.

wo es mindestens ein Argument geben muss .

Eine Musterbindung ist eine Deklaration des Formulars:

<pattern> = expr

Wieder Modulo Wachen.

Beachten Sie, dass Variablen Muster sind , also die Bindung:

plus = (+)

ist eine Musterbindung . Es bindet das Muster plus(eine Variable) an den Ausdruck (+).

Wenn eine Musterbindung nur aus einem Variablennamen besteht, spricht man von einer einfachen Musterbindung.

Die Monomorphismus-Einschränkung gilt für einfache Musterbindungen!

Nun, formal sollten wir das sagen:

Eine Deklarationsgruppe ist eine minimale Menge von voneinander abhängigen Bindungen.

Abschnitt 4.5.1 des Berichts .

Und dann (Abschnitt 4.5.5 des Berichts ):

Eine bestimmte Deklarationsgruppe ist genau dann uneingeschränkt, wenn:

  1. Jede Variable in der Gruppe ist durch eine Funktionsbindung (z. B. f x = x) oder eine einfache Musterbindung (z plus = (+). B. Abschnitt 4.4.3.2) gebunden

  2. Für jede Variable in der Gruppe, die durch einfache Musterbindung gebunden ist, wird eine explizite Typensignatur angegeben. (zB plus :: Num a => a -> a -> a; plus = (+)).

Beispiele von mir hinzugefügt.

Eine eingeschränkte Deklarationsgruppe ist also eine Gruppe, in der entweder nicht einfache Musterbindungen (z. B. (x:xs) = f somethingoder (f, g) = ((+), (-))) oder eine einfache Musterbindung ohne Typensignatur (wie in plus = (+)) vorhanden sind.

Die Monomorphismusbeschränkung betrifft eingeschränkte Deklarationsgruppen.

Meistens definieren Sie keine gegenseitigen rekursiven Funktionen und daher wird eine Deklarationsgruppe nur zu einer Bindung.

Was tut es?

Die Monomorphismusbeschränkung wird in Abschnitt 4.5.5 des Berichts durch zwei Regeln beschrieben .

Erste Regel

Die übliche Hindley-Milner-Einschränkung des Polymorphismus besteht darin, dass nur Typvariablen verallgemeinert werden dürfen, die in der Umgebung nicht frei vorkommen. Darüber hinaus können die eingeschränkten Typvariablen einer eingeschränkten Deklarationsgruppe im Generalisierungsschritt für diese Gruppe möglicherweise nicht verallgemeinert werden. (Denken Sie daran, dass eine Typvariable eingeschränkt ist, wenn sie zu einer Typklasse gehören muss; siehe Abschnitt 4.5.2.)

Der hervorgehobene Teil ist das, was die Monomorphismusbeschränkung einführt. Es heißt, wenn der Typ polymorph ist (dh eine Typvariable enthält) und diese Typvariable eingeschränkt ist (dh eine Klassenbeschränkung hat: z. B. ist der Typ Num a => a -> a -> apolymorph, weil er enthält, aund auch eingeschränkt, weil der Typ adie Einschränkung Numdarüber hat .) dann kann es nicht verallgemeinert werden.

In einfachen Worten bedeutet nicht verallgemeinern , dass die Verwendung der Funktion plusihren Typ ändern kann.

Wenn Sie die Definitionen hätten:

plus = (+)

x :: Integer
x = plus 1 2

y :: Double
y = plus 1.0 2

dann würden Sie einen Tippfehler bekommen. Denn wenn der Compiler sieht, dass plusüber ein Integerin der Deklaration von aufgerufen xwird, wird die Typvariable amit vereinheitlicht Integerund daher wird der Typ von plus:

Integer -> Integer -> Integer

Wenn dann die Definition von typ überprüft ywird, plus wird angezeigt, dass sie auf ein DoubleArgument angewendet wird und die Typen nicht übereinstimmen.

Beachten Sie, dass Sie weiterhin verwenden können, plusohne einen Fehler zu erhalten:

plus = (+)
x = plus 1.0 2

In diesem Fall pluswird zuerst auf den Typ von geschlossen , Num a => a -> a -> a aber dann wird seine Verwendung in der Definition von x, wo 1.0eine Fractional Einschränkung erforderlich ist , in geändert Fractional a => a -> a -> a.

Begründung

Der Bericht sagt:

Regel 1 ist aus zwei Gründen erforderlich, die beide ziemlich subtil sind.

  • Regel 1 verhindert, dass Berechnungen unerwartet wiederholt werden. Zum Beispiel genericLengthist eine Standardfunktion (in der Bibliothek Data.List), deren Typ durch gegeben ist

    genericLength :: Num a => [b] -> a
    

    Betrachten Sie nun den folgenden Ausdruck:

    let len = genericLength xs
    in (len, len)
    

    Es sieht so aus, als ob lenes nur einmal berechnet werden sollte, aber ohne Regel 1 könnte es zweimal berechnet werden, jeweils einmal bei zwei verschiedenen Überladungen. Wenn der Programmierer tatsächlich möchte, dass die Berechnung wiederholt wird, kann eine explizite Typensignatur hinzugefügt werden:

    let len :: Num a => a
        len = genericLength xs
    in (len, len)
    

Für diesen Punkt ist das Beispiel aus dem Wiki meines Erachtens klarer. Betrachten Sie die Funktion:

f xs = (len, len)
  where
    len = genericLength xs

Wenn lenpolymorph fwäre, wäre die Art von :

f :: Num a, Num b => [c] -> (a, b)

Die beiden Elemente des Tupels (len, len)können also tatsächlich unterschiedliche Werte sein! Dies bedeutet jedoch, dass die Berechnung von wiederholt werden genericLength muss , um die beiden unterschiedlichen Werte zu erhalten.

Das Grundprinzip hier ist: Der Code enthält einen Funktionsaufruf, aber wenn diese Regel nicht eingeführt wird, können zwei versteckte Funktionsaufrufe erzeugt werden, was nicht intuitiv ist.

Mit der Monomorphismus-Beschränkung wird die Art von f:

f :: Num a => [b] -> (a, a)

Auf diese Weise muss die Berechnung nicht mehrmals durchgeführt werden.

  • Regel 1 verhindert Mehrdeutigkeiten. Betrachten Sie beispielsweise die Deklarationsgruppe

    [(n, s)] = liest t

    Denken Sie daran, dass dies readseine Standardfunktion ist, deren Typ durch die Signatur angegeben wird

    liest :: (Lesen Sie a) => String -> [(a, String)]

    Ohne Regel 1 nwürde der Typ ∀ a. Read a ⇒ aund s der Typ zugewiesen ∀ a. Read a ⇒ String. Letzteres ist ein ungültiger Typ, da er von Natur aus mehrdeutig ist. Es ist nicht möglich zu bestimmen, bei welcher Überladung verwendet werden soll s, und dies kann auch nicht durch Hinzufügen einer Typensignatur für gelöst werden s. Wenn also nicht einfache Musterbindungen verwendet werden (Abschnitt 4.4.3.2), sind die abgeleiteten Typen in ihren eingeschränkten Typvariablen immer monomorph, unabhängig davon, ob eine Typensignatur bereitgestellt wird. In diesem Fall sind beide nund smonomorph in a.

Ich glaube, dieses Beispiel ist selbsterklärend. Es gibt Situationen, in denen die Nichtanwendung der Regel zu einer Mehrdeutigkeit des Typs führt.

Wenn Sie die Erweiterung wie oben vorgeschlagen deaktivieren, wird beim Kompilieren der obigen Deklaration ein Typfehler angezeigt. Dies ist jedoch kein wirkliches Problem: Sie wissen bereits, dass readSie dem Compiler bei der Verwendung irgendwie mitteilen müssen, welchen Typ er zu analysieren versuchen soll ...

Zweite Regel

  1. Alle monomorphen Typvariablen, die verbleiben, wenn die Typinferenz für ein gesamtes Modul abgeschlossen ist, werden als mehrdeutig betrachtet und unter Verwendung der Standardregeln in bestimmte Typen aufgelöst (Abschnitt 4.3.4).

Das bedeutet, dass. Wenn Sie Ihre übliche Definition haben:

plus = (+)

Dies hat einen Typ, Num a => a -> a -> abei dem aes sich aufgrund der oben beschriebenen Regel 1 um eine monomorphe Typvariable handelt. Sobald das gesamte Modul abgeleitet ist, wählt der Compiler einfach einen Typ aus, der diesen a gemäß den Standardregeln ersetzt.

Das Endergebnis ist : plus :: Integer -> Integer -> Integer.

Beachten Sie, dass dies erfolgt, nachdem das gesamte Modul abgeleitet wurde.

Dies bedeutet, wenn Sie die folgenden Erklärungen haben:

plus = (+)

x = plus 1.0 2.0

innerhalb eines Moduls, bevor geben Sie den Typ des säumigen pluswird: Fractional a => a -> a -> a(siehe Regel 1 , warum dies geschieht). An diesem Punkt wird das Befolgen der Standardregeln adurch ersetzt Double und wir haben plus :: Double -> Double -> Doubleund x :: Double.

Standardmäßig

Wie bereits erwähnt , bevor einige gibt es säumige Regeln, beschrieben in Abschnitt 4.3.4 des Berichts , dass die Rückschließer annehmen können , und das wird einen polymorphen Typen mit einem monomorphic ersetzen. Dies geschieht immer dann, wenn ein Typ nicht eindeutig ist .

Zum Beispiel im Ausdruck:

let x = read "<something>" in show x

hier ist der Ausdruck mehrdeutig, weil die Typen für showund readsind:

show :: Show a => a -> String
read :: Read a => String -> a

Das xhat also Typ Read a => a. Aber diese Einschränkung wird durch eine Menge von Arten erfüllt ist : Int, Doubleoder ()zum Beispiel. Welches soll ich wählen? Nichts kann uns sagen.

In diesem Fall können wir die Mehrdeutigkeit beheben, indem wir dem Compiler mitteilen, welchen Typ wir möchten, und eine Typensignatur hinzufügen:

let x = read "<something>" :: Int in show x

Das Problem ist nun: Da Haskell die NumTypklasse verwendet, um Zahlen zu verarbeiten, gibt es viele Fälle, in denen numerische Ausdrücke Mehrdeutigkeiten enthalten.

Erwägen:

show 1

Was soll das Ergebnis sein?

Wie zuvor 1hat Typ Num a => aund es gibt viele Arten von Zahlen, die verwendet werden könnten. Welches soll ich wählen?

Fast jedes Mal, wenn wir eine Zahl verwenden, einen Compilerfehler zu haben, ist keine gute Sache, und daher wurden die Standardregeln eingeführt. Die Regeln können mithilfe einer defaultDeklaration gesteuert werden . Durch Angabe können default (T1, T2, T3)wir ändern, wie der Inferencer die verschiedenen Typen standardmäßig verwendet.

Eine mehrdeutige Typvariable vist standardmäßig verfügbar, wenn:

  • verscheint nur in Beschränkungen der Art, in der C ves sich Cum eine Klasse handelt (dh wenn es wie Monad (m v)folgt aussieht : dann ist es nicht standardmäßig).
  • Mindestens eine dieser Klassen ist Numoder eine Unterklasse von Num.
  • Alle diese Klassen sind im Prelude oder in einer Standardbibliothek definiert.

Eine voreingestellte Typvariable wird durch den ersten Typ in der defaultListe ersetzt, der eine Instanz aller Klassen der mehrdeutigen Variablen ist.

Die Standarddeklaration defaultlautet default (Integer, Double).

Zum Beispiel:

plus = (+)
minus = (-)

x = plus 1.0 1
y = minus 2 1

Die abgeleiteten Typen wären:

plus :: Fractional a => a -> a -> a
minus :: Num a => a -> a -> a

die standardmäßig zu folgenden Regeln werden:

plus :: Double -> Double -> Double
minus :: Integer -> Integer -> Integer

Beachten Sie, dass dies erklärt, warum im Beispiel in der Frage nur die sort Definition einen Fehler auslöst. Der Typ Ord a => [a] -> [a]kann nicht als Standard festgelegt werden, da Ordes sich nicht um eine numerische Klasse handelt.

Erweiterte Standardeinstellung

Beachten Sie, dass GHCi kommt mit erweiterten säumigen Regeln (oder hier für GHC8 ), die in Dateien aktiviert werden können , als auch die Verwendung von ExtendedDefaultRulesErweiterungen.

Der ausfall Typ Variablen muß nicht nur in contraints erscheinen , wo alle Klassen Standard und es muss mindestens eine Klasse sein , die unter ist Eq, Ord, Showoder Numund ihre Unterklassen.

Darüber hinaus defaultlautet die Standarddeklaration default ((), Integer, Double).

Dies kann zu merkwürdigen Ergebnissen führen. Nehmen Sie das Beispiel aus der Frage:

Prelude> :set -XMonomorphismRestriction
Prelude> import Data.List(sortBy)
Prelude Data.List> let sort = sortBy compare
Prelude Data.List> :t sort
sort :: [()] -> [()]

In ghci erhalten wir keinen Typfehler, aber die Ord aEinschränkungen führen zu einer Standardeinstellung, ()die so gut wie nutzlos ist.

Nützliche Links

Es gibt viele Ressourcen und Diskussionen über die Monomorphismusbeschränkung.

Hier sind einige Links, die ich nützlich finde und die Ihnen helfen können, das Thema zu verstehen oder weiter zu vertiefen:

Bakuriu
quelle