Find out the type of an expression/function with typed holes
Published on
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à!
(Note that 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 `asTypeOf` _
:
...
let leave = branch testName False `asTypeOf` _
...
and 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
let
or where
bindings group and play with
-fmax-relevant-binds=N
and
-fno-max-relevant-binds
.