34

Let us consider the following code snippet:

blah :: a -> b -> a
blah x y = ble x where
    ble :: b -> b
    ble x = x

This compiles fine under GHC, which essentially means that b from the 3rd line is something different than b from the first line.

My question is simple: is there a way to somehow relate in the type declaration of ble to a type used in an outer context, i.e. the type declaration of blah?

Obviously, this is just an example and not a real-world use-case for type declarations.

julx
  • 8,694
  • 6
  • 47
  • 86
  • 1
    that solves a question i never dared to ask - how to make type annotation in where clauses - thx ;-) +1 of course – epsilonhalbe Dec 06 '11 at 21:13
  • 8
    @epsilonhalbe: If you think about it, you've been making type declarations in where clauses all along: you start each program with `module Blarg where ...` :) – Tikhon Jelvis Dec 06 '11 at 21:40
  • @TikhonJelvis whooo i didn't think about that either - maybe it's 'realizing-facts-about-where-clauses'-day ;-) – epsilonhalbe Dec 06 '11 at 22:40

2 Answers2

45

This is possible with the ScopedTypeVariables extension. You need to use explicit forall's to bring the type variables into scope.

blah :: forall a b. a -> b -> a
blah x y = ble x where
    ble :: b -> b
    ble x = x

Trying to load this definition with ScopedTypeVariables enabled gives:

foo.hs:2:16:
    Couldn't match type `a' with `b'
      `a' is a rigid type variable bound by
          the type signature for blah :: a -> b -> a at foo.hs:2:1
      `b' is a rigid type variable bound by
          the type signature for blah :: a -> b -> a at foo.hs:2:1
    In the first argument of `ble', namely `x'
    In the expression: ble x
    In an equation for `blah':
        blah x y
          = ble x
          where
              ble :: b -> b
              ble x = x

You can tell that GHC interprets the two bs as the same type because the error says that a and b are bound on the same line.

John L
  • 27,937
  • 4
  • 73
  • 88
16

If you don't want to use ScopedTypeVariables, you can use the good ole fashion asTypeOf function.

-- defined in Prelude
asTypeOf :: a -> a -> a
x `asTypeOf` y = x

blah :: a -> b -> a
blah x y = ble x where
    ble x = x `asTypeOf` y

Of course, this won't compile because of the type error.

Update:

I would like to point out that sometimes you might have to be a little crafty to do what you want with asTypeOf. Take the following example that superflously uses asTypeOf because I don't want to think of a case that actually needs asTypeOf. Similar solutions would work the same for real world cases.

foo :: Bounded a => Maybe a -> a
foo m = x
  where
    x = maxBound -- Q: how do I make (x :: a) when given (Maybe a)?
    _ = Just x `asTypeof` m -- A: witchcraft!
Community
  • 1
  • 1
Thomas Eding
  • 35,312
  • 13
  • 75
  • 106
  • That's nice, as I understand `asTypeOf` is something like `asTypeOf :: a -> a -> a` with an implementation `asTypeOf x y = x`. – julx Dec 06 '11 at 20:52
  • 1
    That's the same thing as the definition I supplied (only that I wrote it in an infix manner). – Thomas Eding Dec 06 '11 at 20:59
  • 1
    I tend to call this providing a witness, but I'm not sure what the more proper name for the technique is. Just a way to pass in the dictionary, but don't know if there's a short name for it. – Christopher Done Feb 25 '13 at 17:16