Which dictionary does GHC choose when more than one is in scope?

后端 未结 3 1175
臣服心动
臣服心动 2021-01-11 16:51

Consider the following example:

import Data.Constraint

class Bar a where
  bar :: a -> a

foo :: (Bar a) => Dict (Bar a) -> a -> a
foo Dict = ba         


        
3条回答
  •  孤城傲影
    2021-01-11 17:33

    Here's a test:

    {-# LANGUAGE FlexibleInstances, OverlappingInstances, IncoherentInstances #-}
    import Data.Constraint
    
    class    C a    where foo :: a -> String
    instance C [a]  where foo _ = "[a]"
    instance C [()] where foo _ = "[()]"
    
    aDict :: Dict (C [a])
    aDict = Dict
    
    bDict :: Dict (C [()])
    bDict = Dict
    
    bar1 :: String
    bar1 = case (bDict, aDict :: Dict (C [()])) of
             (Dict,Dict) -> foo [()]              -- output: "[a]"
    
    bar2 :: String
    bar2 = case (aDict :: Dict (C [()]), bDict) of
             (Dict,Dict) -> foo [()]              -- output: "[()]"
    

    GHC above happens to use the "last" dictionary which was brought into scope. I wouldn't rely on this, though.

    If you limit yourself to overlapping instances, only, then you wouldn't be able to bring in scope two different dictionaries for the same type (as far as I can see), and everything should be fine since the choice of the dictionary becomes immaterial.

    However, incoherent instances are another beast, since they allow you to commit to a generic instance and then use it at a type which has a more specific instance. This makes it very hard to understand which instance will be used.

    In short, incoherent instances are evil.


    Update: I ran some further tests. Using only overlapping instances and an orphan instance in a separate module you can still obtain two different dictionaries for the same type. So, we need even more caveats. :-(

提交回复
热议问题