Skip to content

Implicit parameters can’t be polymorphic even if QuantifiedConstraints and ImpredicativeTypes are enabled; if this is intentional, it is poorly communicated

Summary

  1. GHC throws a parse error when forall is used inside an implicit parameter constraint without explicit parentheses.
    x = ?y (1 :: Integer)
    x :: (?y::forall t. t -> t) => Integer
    error: parse error on input ‘forall’
  2. GHC refuses to narrow polymorphic implicit parameters.
    x = ?y (1 :: Integer)
    x :: (?y::(forall t. t -> t)) => Integer
    error:
    • Couldn't match type: forall t. t -> t
                     with: t0 -> Integer
        arising from a functional dependency between constraints:
          ‘?y::t0 -> Integer’
            arising from a use of implicit parameter ‘?y’ at
          ‘?y::forall t. t -> t’
            arising from the type signature for:
                           x :: (?y::forall t. t -> t) => Integer at
    • In the expression: ?y
      In the expression: ?y 1
      In an equation for ‘x’: x = ?y 1
  3. GHC unnecessarily specializes polymorphic implicit parameter declarations
    let ?id = id in (?id 1, ?id True) :: (Integer, Bool)
    • Couldn't match type ‘Integer’ with ‘Bool’
        arising from a functional dependency between constraints:
          ‘?id::Bool -> Bool’
            arising from a use of implicit parameter ‘?id’ at
          ‘?id::Integer -> Integer’
            arising from the implicit-parameter binding for ?id at
    • In the expression: ?id
      In the expression: ?id True
      In the expression: (?id 1, ?id True) :: (Integer, Bool)
  4. GHC produces multiple undeclared implicit parameter errors for the same parameters if it is used as different types.
    x = (?i (1 :: Integer), ?i (), ?i True)
    error:
    • Unbound implicit parameter (?i::Integer -> c)
        arising from a use of implicit parameter ‘?i’
    • In the expression: ?i
      In the expression: ?i (1 :: Integer)
      In the expression: (?i (1 :: Integer), ?i (), ?i True)
    • Relevant bindings include
        x :: (c, c, c) (bound at)
    
    error:
    • Unbound implicit parameter (?i::() -> c)
        arising from a use of implicit parameter ‘?i’
    • In the expression: ?i
      In the expression: ?i ()
      In the expression: (?i (1 :: Integer), ?i (), ?i True)
    • Relevant bindings include
        x :: (c, c, c) (bound at)
    
    error:
    • Unbound implicit parameter (?i::Bool -> c)
        arising from a use of implicit parameter ‘?i’
    • In the expression: ?i
      In the expression: ?i True
      In the expression: (?i (1 :: Integer), ?i (), ?i True)
    • Relevant bindings include
        x :: (c, c, c) (bound at)
  5. All of these error messages do not make it clear that polymorphic implicit parameters aren’t supported. In particular, the use of the term “functional dependency” makes this appear related to -XFunctionalDependencies. Therefore: They should either be supported, or the error messages clarified.

Detailed explanation

When enabling the extensions ImplicitParams, QuantifiedConstraints & ImpredicativeTypes, one might expect implicit parameters to be able to carry polymorphic values. This is not the case.

Consider the following example:

example = (?const (), ?const True)  --example: ?const = \_ -> ()

Here, ?const is applied to two values of different types. If we replace the implicit parameter ?const with just const (), this would type-check fine:

example = (const () (), const () True) :: ((), ())

However, GHC seems to infer the type of both uses of ?const separately:

ghci> example = (?const (), ?const True)

<interactive>:11:12: error:
    • Unbound implicit parameter (?const::() -> b)
        arising from a use of implicit parameter ‘?const’
    • In the expression: ?const
      In the expression: ?const ()
      In the expression: (?const (), ?const True)
    • Relevant bindings include
        example :: (b, b) (bound at <interactive>:11:1)

<interactive>:11:23: error:
    • Unbound implicit parameter (?const::Bool -> b)
        arising from a use of implicit parameter ‘?const’
    • In the expression: ?const
      In the expression: ?const True
      In the expression: (?const (), ?const True)
    • Relevant bindings include
        example :: (b, b) (bound at <interactive>:11:1)

This seems like it could be fixed by explicitly typing the implicit parameter. However, unexpectedly, a first attempt at this may fail, since forall in an implicit parameter constraint is a syntax error unless surrounded by parentheses.

ghci> :{
ghci| example = (?const (), ?const True)
ghci| example :: (?const::forall t. t -> b) => (b, b)
ghci| :}

<interactive>:16:21: error: parse error on input ‘forall’

Even after adding parentheses, you’ll find that GHC doesn’t attempt to properly match the polymorphic and monomorphic types:

ghci> { example = (?const (), ?const True); example :: (?const::(forall t. t -> b)) => (b, b) }

<interactive>:19:14: error:
    • Couldn't match type: forall t. t -> b
                     with: () -> b
        arising from a functional dependency between constraints:
          ‘?const::() -> b’
            arising from a use of implicit parameter ‘?const’ at <interactive>:19:14-19
          ‘?const::forall t. t -> b’
            arising from the type signature for:
                           example :: forall b.
                                      (?const::forall t. t -> b) =>
                                      (b, b) at <interactive>:19:39-87
    • In the expression: ?const
      In the expression: ?const ()
      In the expression: (?const (), ?const True)
    • Relevant bindings include
        example :: (b, b) (bound at <interactive>:19:3)

<interactive>:19:25: error:
    • Couldn't match type: forall t. t -> b
                     with: Bool -> b
        arising from a functional dependency between constraints:
          ‘?const::Bool -> b’
            arising from a use of implicit parameter ‘?const’ at <interactive>:19:25-30
          ‘?const::forall t. t -> b’
            arising from the type signature for:
                           example :: forall b.
                                      (?const::forall t. t -> b) =>
                                      (b, b) at <interactive>:19:39-87
    • In the expression: ?const
      In the expression: ?const True
      In the expression: (?const (), ?const True)
    • Relevant bindings include
        example :: (b, b) (bound at <interactive>:19:3)

Inversely, GHC specializes too quickly when implicit parameters are bound.

ghci> let ?id = id in (?id 1, ?id True) :: (Integer, Bool)

<interactive>:20:25: error:
    • Couldn't match type ‘Integer’ with ‘Bool’
        arising from a functional dependency between constraints:
          ‘?id::Bool -> Bool’
            arising from a use of implicit parameter ‘?id’ at <interactive>:20:25-27
          ‘?id::Integer -> Integer’
            arising from the implicit-parameter binding for ?id at <interactive>:20:1-52
    • In the expression: ?id
      In the expression: ?id True
      In the expression: (?id 1, ?id True) :: (Integer, Bool)

One might attempt to solve this problem by placing the forall at the start of the implicit parameter constraint. This only defers the problem to the use site.

ghci> { example = (?const (), ?const True); example :: (forall t. ?const::t -> b) => (b, b) }
ghci> let ?const = \_ -> 1 :: Integer in example

<interactive>:25:36: error:
    • Couldn't match type ‘p0’ with ‘t’
        arising from a functional dependency between constraints:
          ‘?const::t -> Integer’
            arising from a use of ‘example’ at <interactive>:25:36-42
          ‘?const::p0 -> Integer’
            arising from the implicit-parameter binding for ?const at <interactive>:25:1-42
        because type variable ‘t’ would escape its scope
      This (rigid, skolem) type variable is bound by
        a quantified context
        at <interactive>:25:1-42
    • In the expression: example
      In the expression: let ?const = \ _ -> ... in example
      In an equation for ‘it’: it = let ?const = \ _ -> ... in example

Environment

  • GHC version used: 9.2.1

Optional:

  • Operating System: NixOS 22.05.20220107.ff377a7 (Quokka)
    • GHC was a binary cache download used in a nix shell environment, selected Nixpkgs attribute haskell.packages.ghc921.ghc
  • System Architecture: Linux buggeryyacht-nixos 5.15.12 #1-NixOS SMP Wed Dec 29 11:29:03 UTC 2021 x86_64 GNU/Linux
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information