March 13, 2014
An often asked Haskell question is how to find out a type of a locally defined function or expression.
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
... let leave = branch testName False ...
and I wanted to see what the type of
leave is. So I appended
... let leave = branch testName False `asTypeOf` _ ...
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