如何使用适用于异构集合中每种类型的约束?

How to use constraints that apply to each type in a heterogenous collection?

我正在尝试找出异构集合,但有一些类型检查问题我无法解决。

data HTypeMap (f :: * -> *) (types :: [*]) where
  HNil :: HTypeMap f '[]
  HCons :: f e -> HTypeMap f types -> HTypeMap f (e ': types)

(它基本上应该是在某个函子 f 下从类型到该类型的值的映射)。例如:

testMap :: HTypeMap Prelude.Maybe '[Prelude.Int, Prelude.String]
testMap = HCons (Prelude.Just 3) (HCons (Prelude.Just "Hello") HNil)

我一直在尝试制定一个函数,允许我 运行 和 map 以防万一我知道我的集合中的所有类型都受到限制,可以像这样使用:

showEverything :: AllConstrained Show types => HTypeMap f types -> [String]
showEverything = mapConstrained show


type family AllConstrained (c :: u -> Constraint) (ts :: [u]) :: Constraint where
  AllConstrained c '[] = ()
  AllConstrained c (t ': ts) = (c t, AllConstrained c ts)


我目前的尝试:

  1. 只是一个简单的函数...GHC 不会让我写这个类型:
mapConstrained :: AllConstrained c types => (forall t. c t => f t -> a) -> HTypeMap f types -> [a]
mapConstrained = _

它说:

• Could not deduce: AllConstrained c0 types
  from the context: AllConstrained c types

我完全不明白...我只有一个限制条件需要考虑,它是 c。 (我有 ScopedTypeVariables。)

相同样式的类型类也是如此...这里有一些我没有注意到的歧义...

2.

这个笨重的工具确实有效:

data ContraintRunnerF f c r = ContraintRunnerF {runMeF :: forall x. c x => f x -> r}

class MapConstrained c types where
  mapConstrained :: forall a f r. AllConstrained c types => ContraintRunnerF f c r -> HTypeMap f types -> [r]

instance MapConstrained c '[] where
  mapConstrained _ HNil = []

instance MapConstrained c types => MapConstrained c (t ': types) where
  mapConstrained g (HCons e rest) = (runMeF g e) : mapConstrained @c g rest

像这样使用:


showRunnerF :: ContraintRunnerF Prelude.Identity Prelude.Show Prelude.String
showRunnerF = ContraintRunnerF Prelude.show

showRunnerFM :: ContraintRunnerF Prelude.Maybe Prelude.Show Prelude.String
showRunnerFM = ContraintRunnerF Prelude.show

testMap :: HTypeMap Prelude.Maybe '[Prelude.Int, Prelude.String]
testMap = HCons (Prelude.Just 3) (HCons (Prelude.Just "Hello") HNil)

test1 :: [Prelude.String]
test1 = mapConstrained showRunnerFM testMap

但不幸的是,即使是简单的替换也不会进行类型检查,基本上需要我指定所有类型:

test2 :: [Prelude.String]
test2 = mapConstrained (ContraintRunnerF Prelude.show) testMap

• Could not deduce: (c0 Prelude.Int, c0 [Prelude.Char])
    arising from a use of ‘mapConstrained’

所以,问题:

  1. 我如何理解 GHC 在这些方法中存在的问题?
  2. 我该如何制作满足我需求的功能?

类型变量 cmapConstrained 的类型中被称为 ambiguous 因为从来没有任何方法可以确定该类型变量应该来自什么这是争论。

这个问题的解决方案是TypeApplications,它允许您为不明确的类型变量显式指定类型。

{-# LANGUAGE AllowAmbiguousTypes, TypeApplications, PolyKinds #-}

import Data.Kind

data HTypeMap (f :: * -> *) (types :: [*]) where
  HNil :: HTypeMap f '[]
  HCons :: f e -> HTypeMap f types -> HTypeMap f (e ': types)

type family AllConstrained (c :: u -> Constraint) (ts :: [u]) :: Constraint where
  AllConstrained c '[] = ()
  AllConstrained c (t ': ts) = (c t, AllConstrained c ts)

mapConstrained :: AllConstrained c types => (forall t. c t => f t -> a) -> HTypeMap f types -> [a]
mapConstrained _ _  = undefined

testMap :: HTypeMap Prelude.Maybe '[Prelude.Int, Prelude.String]
testMap = HCons (Prelude.Just 3) (HCons (Prelude.Just "Hello") HNil)

-- Note here that `@Show' is the type application - it applies `mapConstrained' at the type level
test = mapConstrained @Show show testMap

在第二种情况下,您必须再次明确指定类型变量:

test2 :: [Prelude.String]
test2 = mapConstrained (ContraintRunnerF @_ @Show Prelude.show) testMap