Home > Net >  Heterogeneous list / HList l -> [e]?
Heterogeneous list / HList l -> [e]?

Time:03-04

Thanks for the answers of my other quesion - How to write a Heterogeneous list on HList? , I could start using HList: Heterogeneous lists, mostly with API of https://hackage.haskell.org/package/HList-0.5.2.0/docs/Data-HList-HList.html

Now, I want - producing homogenous lists as HList l -> [e]

hMapOut :: forall f e l. HMapOut f l e => f -> HList l -> [e]

I've been trying create my own proof of concept code, but don't know how to do it properly.

import Data.HList (HList, hBuild, hEnd, hMap, hMapOut)

iA :: [Int]
iA = [1, 2, 3] :: [Int]

iB :: [[Char]]
iB = ["foo", "bar"] :: [[Char]]

iAiB :: HList '[[Int], [[Char]]]
iAiB = hEnd $ hBuild iA iB

flag :: Any -> Bool
flag = \iX ->  length iX >= 3  --- ERROR HERE

flags :: HList l -> [e]
flags = \iList ->
  iList & hMapOut flag

iBool :: [e]
iBool = flags iAiB

main :: IO ()
main = print iBool

This is the simplest example, and for my purpose, it needs to be wrapped into IO, in either way, it has the identical error.

• Couldn't match expected type ‘t0 a0’ with actual type ‘Any’
• In the first argument of ‘length’, namely ‘x’
  In the first argument of ‘(>=)’, namely ‘length x’
  In the expression: length x >= 3 typecheck(-Wdeferred-type-errors)

I suppose Any type is wrong, but I don't know how to type. Advice?


I also did

flag :: forall (t :: * -> *) a. Foldable t => t a -> Bool
flag = \iX ->  length iX >= 3

This eliminates the error for this function, but now another error on

flags :: HList l -> [e]
flags = \iList ->
  iList & hMapOut flag --- ERROR HERE

as

• Ambiguous type variables ‘t0’,
                           ‘a0’ arising from a use of ‘hMapOut’
  prevents the constraint ‘(Data.HList.HList.HFoldr

So, which seems to be the fundamental problem.


tried Fun'

import Data.HList (HList, hBuild, hEnd, hMap, hMapOut, Fun' (Fun'))

flags :: HList l -> [e]
flags = \iList ->
  iList & hMapOut (Fun' flag :: Fun' Foldable Bool)

error:

• Could not deduce: b ~ Bool
  from the context: Data.HList.FakePrelude.FunCxt Foldable b
    bound by a type expected by the context:
               forall b.
               Data.HList.FakePrelude.FunCxt Foldable b =>
               Data.HList.FakePrelude.FunApp Bool b -> b

CodePudding user response:

Your second version, flag :: ∀ t a. Foldable t => t a -> Bool, is sensible. But mapping this over a HList is a bit tricky because the constraint doesn't have the form Type -> Constraint, instead it's the application of t with a (Type -> Type) -> Constraint.

Since you don't seem to require supporting generic foldable containers (and FWIW the Foldable-based definition of length is a bit dubious anyway) I'd suggest instead using the IsList class:

flag :: ∀ l. IsList l => l -> Bool
flag = (>=3) . length . toList

Alternatively, we can quickly wrap Foldable into our own class with Type -> Constraint signature:

class HasLength l where gLength :: l -> Int
instance Foldable t => HasLength (t a) where gLength = length

flag :: ∀ l. HasLength l => l -> Bool
flag = (>=3) . gLength

Now this is easy enough to fold over a HList:

flags :: HMapOut (Fun IsList Bool) l Bool
           => HList l -> [Bool]
flags = hMapOut (Fun flag :: Fun IsList Bool)

Note that this is Fun, not Fun'. The latter doesn't work in this situation (and generally with HMapOut), because the function is polymorphic in its input, i.e. it can't be possible to determine the input type from the output one.

Again, I'm inclined to define a helper to make this sort of definition more convenient:

h'MapOut :: ∀ (cxt :: Type -> Constraint) getb l
           . HMapOut (Fun cxt getb) l getb
         => (∀ a . (cxt a) => a -> getb) -> HList l -> [getb]
h'MapOut f = hMapOut (Fun f :: Fun cxt getb)

and then simply

flags = h'MapOut @IsList flag

Full code:

{-# LANGUAGE ScopedTypeVariables, UnicodeSyntax
        , KindSignatures, ConstraintKinds, RankNTypes
        , FlexibleContexts, FlexibleInstances
        , AllowAmbiguousTypes, TypeApplications #-}

import Data.HList
import GHC.Exts (IsList(..))
import Data.Kind

flag :: ∀ l. HasLength l => l -> Bool
flag = (>=3) . gLength
    
h'MapOut :: ∀ (cxt :: Type -> Constraint) getb l
           . HMapOut (Fun cxt getb) l getb
         => (∀ a . (cxt a) => a -> getb) -> HList l -> [getb]
h'MapOut f = hMapOut (Fun f :: Fun cxt getb)

flags :: HMapOut (Fun HasLength Bool) l Bool
           => HList l -> [Bool]
flags = h'MapOut @HasLength flag

class HasLength l where gLength :: l -> Int
instance Foldable t => HasLength (t a) where gLength = length
  • Related