简体   繁体   中英

Compiling a type class, Queue, with an instance for lists, don't work due to incomplete inference in GHC 7.8

Trying to compile the following Haskell program:

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}

import qualified Data.List as L

class Queue c e where
    new     :: c
    insert  :: e -> c -> c
    extract :: c -> (e, c)
    empty   :: c -> Bool

instance Queue [e] e where
    new            = []
    insert         = (:)
    extract (x:xs) = (x,xs)
    empty          = L.null

toList :: Queue c e => c -> [e]
toList queue | empty queue = []
toList queue = let (first,rest) = extract queue in first : toList rest

main = print (new ([] :: [Int]))

You get the following error:

[1 of 1] Compiling Main             ( afff.hs, afff.o )

afff.hs:19:16:
    Could not deduce (Queue c e1) arising from a use of ‘empty’
    from the context (Queue c e)
      bound by the type signature for toList :: Queue c e => c -> [e]
      at afff.hs:18:11-31
    The type variable ‘e1’ is ambiguous
    Relevant bindings include
      queue :: c (bound at afff.hs:19:8)
      toList :: c -> [e] (bound at afff.hs:19:1)
    Note: there is a potential instance available:
      instance Queue [e] e -- Defined at afff.hs:12:10
    In the expression: empty queue
    In a stmt of a pattern guard for
                   an equation for ‘toList’:
      empty queue
    In an equation for ‘toList’: toList queue | empty queue = []

afff.hs:22:8:
    No instance for (Show a0) arising from a use of ‘print’
    The type variable ‘a0’ is ambiguous
    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: print (new ([] :: [Int]))
    In an equation for ‘main’: main = print (new ([] :: [Int]))

afff.hs:22:15:
    No instance for (Queue ([Int] -> a0) e0)
      arising from a use of ‘new’
    In the first argument of ‘print’, namely ‘(new ([] :: [Int]))’
    In the expression: print (new ([] :: [Int]))
    In an equation for ‘main’: main = print (new ([] :: [Int]))

shell returned 1

Why GHC isn't able to deduce that "e1 = Int", if that is clearly the case?

It's not clearly the case that (e1 ~ Int) just because (c ~ [Int]) . In order to use a MPTC, GHC needs to know every type parameter for it. The type of empty only refers to one of the type parameters, so it never can be used.

As an aside, I'm pretty firmly of the opinion that it's a bug for GHC to not reject this class definition immediately. It's clearly impossible to use, so why does it wait until you try to use it to reject it?

You might think that just because only one instance matches the c type parameter that you're ok, but you're not. GHC doesn't care what instances are visible during class resolution. It determines what class needs to exist, then checks for it.

In your case, it says "I know what I need c to be, but.. I have no clue what I need e to be. I can't tell what instance I'm supposed to use." As I say in my aside, this problem is obvious in the definition of the class itself.

The most direct way to solve this is with a functional dependency, like this:

class Queue c e | c -> e where
    new     :: c
    insert  :: e -> c -> c
    extract :: c -> (e, c)
    empty   :: c -> Bool

That requires the FunctionalDependencies language extension to use. It specifies to the compiler that the type e is uniquely determined by the type c . That means that it rejects a pair of instances where they have the same type c but different types e , and it assumes that if it knows what type c is that it has enough information to pick which instance to use.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM