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