异构列表/HList l -> [e]?

Heterogeneous list / HList l -> [e]?

感谢您回答我的另一个问题 - , 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

现在,我想要 - producing homogenous lists 作为 HList l -> [e]

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

我一直在尝试创建自己的概念验证代码,但不知道如何正确地进行。

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

这是最简单的示例,为了我的目的,它需要被包装到 IO 中,无论哪种方式,它都有相同的错误。

• 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)

我想Any打错了,但我不知道怎么打。有什么建议?


我也做了

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

这消除了此函数的错误,但现在

上的另一个错误
flags :: HList l -> [e]
flags = \iList ->
  iList & hMapOut flag --- ERROR HERE

作为

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

所以,这似乎是根本问题。


尝试过Fun'

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

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

错误:

• 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

您的第二个版本 flag :: ∀ t a. Foldable t => t a -> Bool 是明智的。但是将其映射到 HList 上有点棘手,因为约束没有 Type -> Constraint 的形式,而是 t(Type -> Type) -> Constraint.

的应用

由于您似乎不需要支持通用的可折叠容器(并且 FWIW Foldable 基于 length 的定义是 ) I'd suggest instead using the IsList class:

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

或者,我们可以使用 Type -> Constraint 签名快速将 Foldable 包装到我们自己的 class 中:

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

现在折叠 HList 很容易了:

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

请注意,这是 Fun,而不是 Fun'。后者在这种情况下不起作用(通常使用 HMapOut),因为该函数在其 输入 中是多态的,即无法确定输入从输出中键入。

同样,我倾向于定义一个助手来使这种定义更方便:

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 = h'MapOut @IsList flag

完整代码:

{-# 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