Home > Mobile >  How to write a Heterogeneous list on HList?
How to write a Heterogeneous list on HList?

Time:03-02

I want to use HList: Heterogeneous lists.

Installed the library then import Data.HList (HList) has been done so far.

Now investigating https://bitbucket.org/HList/hlist/src/master/examples/HListExample/ does not help me to start writing the code.

What I want to do is,

  1. create a hello-world Heterogeneous lists such as H[1, "2"] (not sure about the syntax at all)

  2. map the Heterogeneous lists to print each.

Is there anyone familiar to how to use HList?

CodePudding user response:

You can construct an HList by using the hEnd and hBuild functions, e.g.:

hello = hEnd (hBuild 1 "2")

Mapping print over this list is much more difficult. I think there is no easy way to write a higher order function that is able to map print over the HList, but you can manually traverse the HList like this:

{-# LANGUAGE DataKinds #-}

import Data.HList

class PrintEach ts where
  printEach :: HList ts -> IO ()
instance PrintEach '[] where
  printEach HNil = pure ()
instance (Show t, PrintEach ts) => PrintEach (t : ts) where
  printEach (HCons x xs) = print x *> printEach xs

hello = hEnd (hBuild 1 "2")

main = printEach hello
-- This prints:
-- 1
-- "2"

CodePudding user response:

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"
  • Related