Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeFamilies or GADTs suddenly breaks the valid code

I have very innocent-looking code

data Config = Config
    { cInts    :: [Int]
    , cStrings :: [String] }

instance Semigroup Config where
    c1 <> c2 = Config
        { cInts    = andCombiner cInts
        , cStrings = andCombiner cStrings }
      where
        andCombiner field = field c1 <> field c2

It compiles and works fine. However if I add TypeFamilies or GADTs extension I see very strange error:

.../Main.hs:19:22: error:
    • Couldn't match type ‘Int’ with ‘[Char]’
      Expected type: [String]
        Actual type: [Int]
    • In the ‘cStrings’ field of a record
      In the expression:
        Config {cInts = andCombiner cInts, cStrings = andCombiner cStrings}
      In an equation for ‘<>’:
          c1 <> c2
            = Config
                {cInts = andCombiner cInts, cStrings = andCombiner cStrings}
            where
                andCombiner field = field c1 <> field c2
   |
19 |         , cStrings = andCombiner cStrings }
   |                      ^^^^^^^^^^^^^^^^^^^^

.../Main.hs:19:34: error:
    • Couldn't match type ‘[Char]’ with ‘Int’
      Expected type: Config -> [Int]
        Actual type: Config -> [String]
    • In the first argument of ‘andCombiner’, namely ‘cStrings’
      In the ‘cStrings’ field of a record
      In the expression:
        Config {cInts = andCombiner cInts, cStrings = andCombiner cStrings}
   |
19 |         , cStrings = andCombiner cStrings }
   |                                  ^^^^^^^^

What can be the reason for this compiler error?

like image 937
vrom911 Avatar asked Dec 29 '17 23:12

vrom911


1 Answers

This is due to -XMonoLocalBinds which -XGADTs and -XTypeFamilies imply. You can get your code to compile again by adding a type signature to andCombiner (or by turning on -XNoMonoLocalBinds, although I do not recommend that):

instance Semigroup Config where
    c1 <> c2 = Config
        { cInts    = andCombiner cInts
        , cStrings = andCombiner cStrings }
      where
        andCombiner :: Semigroup a => (Config -> a) -> a
        andCombiner field = field c1 <> field c2

Using the terminology from the GHC docs I've linked, andCombiner is not fully generalized because it mentions c1 and c2 which are not closed or imported.

like image 189
Alec Avatar answered Sep 28 '22 09:09

Alec