web-dev-qa-db-fra.com

À quoi sert l'extension FlexibleContexts? Pourriez-vous l'expliquer à l'aide d'un exemple simple?

J'essayais de comprendre ce que faisait l'extension FlexibleContexts en recherchant des pages Web qui l'expliqueraient à de simples mortels (des gens qui ont lu LYHFGG, par exemple, comme moi), mais je n'ai trouvé aucune ressource de ce type.

Par conséquent Je demande aux experts sur le sujet: pourriez-vous expliquer ce que fait cette extension, pourquoi elle existe, et donner un ou deux exemples simples comment et pourquoi on devrait l'utiliser?

De plus , si je lis le code de quelqu'un d'autre qui utilise cette extension, alors que dois-je savoir sur l'extension afin de comprendre le code écrit en utilisant cette extension?

36
jhegedus

Sans FlexibleContexts toutes les contraintes de classe de types sur les définitions de fonction doivent avoir des variables de type. Par exemple:

add :: Num a => a -> a -> a 
add = (+)

a est la variable de type. Avec FlexibleContexts activé, vous pouvez avoir n'importe quel type dans une classe de types.

intAdd :: Num Int => Int -> Int -> Int 
intAdd = (+)

Cet exemple est assez artificiel mais c'est le plus simple auquel je puisse penser. FlexibleContexts n'est généralement utilisé qu'avec MultiParamTypeClasses. Voici un exemple:

class Shower a b where
  myShow :: a -> b

doSomething :: Shower a String => a -> String
doSomething = myShow

Ici, vous pouvez voir que nous disons que nous voulons seulement un Shower a String. Sans FlexibleContextsString devrait être une variable de type au lieu d'un type concret.

55
DiegoNolan

Généralement, il est utilisé avec l'extension MultiParamTypeClasses, par exemple lorsque vous utilisez la bibliothèque mtl que vous pourriez écrire

doSomethingWithState :: MonadState MyState m => m ()
doSomethingWithState = do
    current <- get
    let something1 = computeSomething1 current
        something2 = computeSomething2 current something1
    put something2

Et de la même façon avec MonadReader et MonadWriter, ainsi que d'autres classes de types similaires. Sans FlexibleContexts, vous ne pouvez pas utiliser cette contrainte.

(Notez que cette réponse était basée sur @ DiegoNolan's mais réécrite pour utiliser une bibliothèque existante qui devrait avoir du sens pour les lecteurs de LYAH).

8
bheklilr

J'ai découvert une utilisation en dehors de celles mentionnées: il en résulte des messages d'erreur plus clairs de GHC. Par exemple. normalement,

Prelude> max (1, 2) 3

<interactive>:1:1: error:
    • Non type-variable argument in the constraint: Num (a, b)
      (Use FlexibleContexts to permit this)
    • When checking the inferred type
        it :: forall a b.
              (Num (a, b), Num b, Num a, Ord b, Ord a) =>
              (a, b)

Et avec FlexibleContexts activé:

Prelude> max (1, 2) 3

<interactive>:1:1: error:
    • No instance for (Num (Integer, Integer))
        arising from a use of ‘it’
    • In the first argument of ‘print’, namely ‘it’
      In a stmt of an interactive GHCi command: print it

Voici ne discussion .

5
Dogweather

FlexibleContexts est souvent utilisé avec les familles de types. Par exemple, lorsque vous utilisez GHC.Generics, il est courant de voir des signatures comme

foo :: (Generic a, GFoo (Rep a)) => Int -> a -> a

Cela peut être vu comme une variation de l'utilisation de MultiParamTypeClasses:

class (Generic a, rep ~ Rep a) => MPGeneric rep a
instance (Generic a, rep ~ Rep a) => MPGeneric rep a

mpFoo :: (MPGeneric rep a, GFoo rep) => Int -> a -> a

Comme AJFarmar l'a souligné , FlexibleContexts est également utile sans MPTC ni familles de types. Voici un exemple simple:

newtype Ap f a = Ap (f a)
deriving instance Show (f a) => Show (Ap f a)

L'approche alternative utilisant Show1 est beaucoup plus gênant.

Un exemple plus impliqué est fourni par le commentaire d'AJFarmar:

data Free f a = Pure a | Free (f (Free f a))
deriving instance (Show a, Show (f (Free f a))) => Show (Free f a)

Cela apporte également UndecidableInstances, car il est récursif, mais il explique bien ce dont il a besoin pour pouvoir montrer Free f a. Dans GHC Hedgeell à fond perdu, une alternative serait d'utiliser QuantifiedConstraints:

deriving instance (Show a, forall x. Show x => Show (f x)) => Show (Free f a)

mais c'est exagéré car nous n'avons qu'à montrer f appliqué à Free f a.

1
dfeuer