Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the FlexibleContexts extension good for? Could you please explain it using a simple example?

Tags:

haskell

ghc

I was trying to understand what the FlexibleContexts extension is doing by searching for web pages that would explain it to mere mortals (people who have read LYHFGG, for example, like me) but I did not find any such resource.

Therefore I ask the experts on the topic: Could you please explain what this extension does, why it exists, and give one or two simple examples how and why one should use it?

Furthermore, If I am reading someone else's code which uses this extension, then what should I know about the extension in order to understand the code written using this extension?

like image 794
jhegedus Avatar asked Jul 06 '15 16:07

jhegedus


4 Answers

Without FlexibleContexts all typeclass constraints on function definitions must have type variables. For example:

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

Where a is the type variable. With FlexibleContexts enabled you can have any type inside a typeclass.

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

This example is pretty contrived but it is the simplest I can think of. FlexibleContexts is usually only used with MultiParamTypeClasses. Here is an example:

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

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

Here you can see we say that we only want a Shower a String. Without FlexibleContexts String would have to be a type variable instead of a concrete type.

like image 110
DiegoNolan Avatar answered Sep 29 '22 23:09

DiegoNolan


Commonly it's used with the MultiParamTypeClasses extension, for example when using the mtl library you might write

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

And similarly with MonadReader and MonadWriter, along with other similar typeclasses. Without FlexibleContexts you can't use this constraint.

(Note that this answer was based on @DiegoNolan's but rewritten to use an existing library that should make sense to LYAH readers).

like image 41
bheklilr Avatar answered Sep 30 '22 23:09

bheklilr


I've discovered a use for it apart from those mentioned: it results in clearer error messages from GHC. E.g. normally,

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)

And with FlexibleContexts enabled:

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

Here's a discussion.

like image 7
Dogweather Avatar answered Sep 29 '22 23:09

Dogweather


FlexibleContexts is often used with type families. For example, when using GHC.Generics, it's common to see signatures like

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

This can be seen as a variation of the MultiParamTypeClasses usage:

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

As AJFarmar pointed out, FlexibleContexts is also useful with neither MPTCs nor type families. Here's a simple example:

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

The alternative approach using Show1 is significantly more awkward.

A more involved example is provided by AJFarmar's comment:

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

This brings in UndecidableInstances as well, since it's recursive, but it does a good job of explaining just what it needs to be able to show Free f a. In bleeding-edge GHC Haskell, an alternative would be to use QuantifiedConstraints:

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

but this is overkill because we only need to show f applied to Free f a.

like image 2
dfeuer Avatar answered Sep 30 '22 23:09

dfeuer