You probably don't want to just map over the HList, but to traverse over it, aka mapM. And you're probably not interested in the individial result-()s but only the side effects, so that would be mapM_ then.
HList does have a general tool for that, called unsurprisingly hMapM_. Using it with arbitrary constrained-polymorphic functions is a bit involved, but for printing specifically this is actually shipped ready-for-use with the library:
Prelude Data.HList> let l = 1.*."2".*.HNil :: HList '[Int, String]
Prelude Data.HList> l
H[1,"2"]
Prelude Data.HList> hMapM_ HPrint l
1
"2"
As Noughtmare commented, the way to use this with other polymorphic functions (without having to write a custom type) is to use Fun, like
Prelude Data.HList> hMapM_ (Fun print :: Fun Show (IO ())) l
1
"2"
...which is not too awkward, but still a bit annoying. I'd define a synonym that allows writing this with TypeApplications to avoid redundancy:
{-# LANGUAGE TypeFamilies, RankNTypes, ConstraintKinds, UnicodeSyntax #-}
import Data.Kind
fun :: ∀ (cxt :: Type -> Constraint) getb
. (∀ a. (cxt a) => a -> getb) -> Fun cxt getb
fun = Fun
and then
Prelude Data.HList Data.Kind> :set -XTypeApplications
Prelude Data.HList Data.Kind> hMapM_ (fun @Show print) l
1
"2"
Or we could even make it
{-# LANGUAGE FlexibleContexts, AllowAmbiguousTypes #-}
h'MapM_ :: ∀ (cxt :: Type -> Constraint) l m
. (Monad m, HFoldr (Mapcar (Fun cxt (m ()))) [m ()] l [m ()])
=> (∀ a. (cxt a) => a -> m ()) -> HList l -> m ()
h'MapM_ f = hMapM_ (Fun f :: Fun cxt (m ()))
and then
Prelude Data.HList Data.Kind> h'MapM_ @Show print l
1
"2"