Since these flexible contexts and instances aren't available in the Haskell standard, I assume there are potential problems when using them. What are they? Can they lead to some ambiguity, undecidability, overlapping instances, etc.?
There is a similar question that asks only about FlexibleInstances
, not FlexibleContexts
, but the answer only says "that it's safe to use them".
I once stumbled upon the following. Answering this question, I first tried this code:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
class (Eq a, Show a) => Genome a where
crossover :: (Fractional b) => b -> a -> a -> IO (a, a)
mutate :: (Fractional b) => b -> a -> IO a
develop :: (Phenotype b a) => a -> b
class (Eq a, Show a) => Phenotype a b | a -> b where
-- In case of Coevolution where each phenotype needs to be compared to
-- every other in the population
fitness :: [a] -> a -> Int
genome :: (Genome b) => a -> b -- here, the problem
breed parents cross mute = do
children <- mapM (\ (dad, mom) -> crossover cross (genome dad) (genome mom))
parents
let ch1 = map fst children ++ map snd children
mutated <- mapM (mutate mute) ch1
return $ map develop mutated
And got a compilation error and a suggestion by GHCi to add the FlexibleContexts
option. When I did, it compiled OK. But this was actually not a right thing to do, as the constraint declaration introduced new scope for type variables, and b
in genome
's type signature became completely unrelated to the one in the type class; yet FlexibleContexts
provided a cover for this.
With the constraint specified properly at the type class level,
class (Eq a, Show a, Genome b) => Phenotype a b | a -> b where
-- In case of Coevolution where each phenotype needs to be compared to
-- every other in the population
fitness :: [a] -> a -> Int
genome :: a -> b
it passed compilation without requiring the FlexibleContexts
option.