An often asked Haskell question is how to find out a type of a locally defined function or expression.
The classic solutions are to specify the wrong type and read the error message, or get help from a tool like hdevtools.
Here’s a new one: use the typed holes feature of GHC 7.8+.
On the surface, typed holes solve a somewhat different problem: find out the desired type of the yet unwritten code, while we want to find out the actual type of the written code.
But typed holes are very easy to adapt for our needs. The
asTypeOf :: a -> a -> a function forces the types of its two arguments to unify. So we can force the type of a hole to be the same as the type of an existing expression — and voilà!
asTypeOf is exported from
Prelude, so typically you don’t have to import anything to bring it in scope. Nor do you have to enable any extensions for typed holes to work.)
In my case I had code like this
and I wanted to see what the type of
leave is. So I appended
ghci told me:
*Test.Tasty.Runners.Html> :r [2 of 2] Compiling Test.Tasty.Runners.Html ( Test/Tasty/Runners/Html.hs, interpreted ) Test/Tasty/Runners/Html.hs:86:58: Found hole ‘_’ with type: Maybe (String, H.AttributeValue) -> H.AttributeValue -> H.AttributeValue -> H.AttributeValue -> H.Markup Relevant bindings include leave :: Maybe (String, H.AttributeValue) -> H.AttributeValue -> H.AttributeValue -> H.AttributeValue -> H.Markup (bound at Test/Tasty/Runners/Html.hs:86:17) mkSummary :: H.Html -> Summary (bound at Test/Tasty/Runners/Html.hs:88:17) status :: Tasty.Status (bound at Test/Tasty/Runners/Html.hs:82:13) i :: IntMap.Key (bound at Test/Tasty/Runners/Html.hs:79:11) testName :: String (bound at Test/Tasty/Runners/Html.hs:78:19) runTest :: t -> String -> t1 -> Traversal (Functor.Compose (t2 IO) (Const Summary)) (bound at Test/Tasty/Runners/Html.hs:78:9) runner :: Tasty.OptionSet -> Tasty.TestTree -> m (IntMap.IntMap (STM.TVar Tasty.Status) -> IO Bool) (bound at Test/Tasty/Runners/Html.hs:72:3) (Some bindings suppressed; use -fmax-relevant-binds=N or -fno-max-relevant-binds) In the second argument of ‘asTypeOf’, namely ‘_’ In the expression: branch testName False `asTypeOf` _ In an equation for ‘leave’: leave = branch testName False `asTypeOf` _ Failed, modules loaded: Paths_tasty_html. *Paths_tasty_html>
As you see, I got not just the type of the hole itself, but also the types of some other relevant definitions — very handy!
Another thing you can do is put
_ = _ inside a
where bindings group and play with