我正在尝试编写一个类型系列,我可以使用它来约束类型级别列表的元素。我有这段代码:
{-# LANGUAGE PolyKinds, DataKinds, TypeOperators, TypeFamilies #-}
import GHC.TypeLits (KnownSymbol, symbolVal)
import GHC.Exts (Constraint)
import Data.Proxy (Proxy(..))
type family AllHave (c :: k -> Constraint) (xs :: [k]) :: Constraint
type instance AllHave c '[] = ()
type instance AllHave c (x ': xs) = (c x, AllHave c xs)
type family Head (xs :: [k]) :: k where
Head (x ': xs) = x
headProxy :: proxy xs -> Proxy (Head xs)
headProxy _ = Proxy
test :: AllHave KnownSymbol xs => proxy xs -> String
test p = symbolVal (headProxy p)
main :: IO ()
main = putStrLn $ test (Proxy :: Proxy '["a", "b"])
根据我的理解,这应该有效,但是当我编译ghc spits out this:
Test.hs:18:10:
Could not deduce (KnownSymbol (Head xs))
arising from a use of ‘symbolVal’
from the context (AllHave KnownSymbol xs)
bound by the type signature for
test :: AllHave KnownSymbol xs => proxy xs -> String
at Test.hs:17:9-52
In the expression: symbolVal (headProxy p)
In an equation for ‘test’: test p = symbolVal (headProxy p)
答案 0 :(得分:4)
这里的问题是Head
在xs
中被test
传送时不会减少,因此Haskell无法从KnownSymbol (Head xs)
中推断出AllHave KnownSymbol xs
。它不应该:xs
为空时会发生什么?
要解决此问题,您可以明确指出xs
不是空的,如下所示:
test :: AllHave KnownSymbol (x ': xs) => proxy (x ': xs) -> String
答案 1 :(得分:2)
我对类型系列知之甚少,所以我会指出gallais's answer来解释代码中出了什么问题。这是一种非常不同的方法,具有许多演示功能。可能有更好的方法;我不知道。
data CList :: (k -> Constraint) -> [k] -> * where
CNil :: CList c '[]
CCons :: c t => proxy t -> CList c ts -> CList c (t ': ts)
mapCSimple :: (forall a . c a => Proxy a -> b) -> CList c as -> [b]
mapCSimple f CNil = []
mapCSimple f (CCons (t :: proxy t) ts) = f (Proxy :: Proxy t) : mapCSimple f ts
toStrings :: CList KnownSymbol v -> [String]
toStrings = mapCSimple symbolVal
class KnownSymbols (xs :: [Symbol]) where
known :: proxy xs -> CList KnownSymbol xs
instance KnownSymbols '[] where
known _ = CNil
instance (KnownSymbol x, KnownSymbols xs) => KnownSymbols (x ': xs) where
known _ = CCons Proxy $ known Proxy
exampleG :: KnownSymbols xs => proxy xs -> String
exampleG p = show . toStrings $ known p
这给出了
> putStrLn $ exampleG (Proxy :: Proxy '["Hello", "Darkness"])
["Hello","Darkness"]
为了得到更像你想要的东西,
cHead :: CList c (a ': as) -> Dict (c a)
cHead (CCons prox _) = Dict
test :: forall x xs . CList KnownSymbol (x ': xs) -> String
test xs = case cHead xs of Dict -> symbolVal (Proxy :: Proxy x)
test2 :: (KnownSymbols xs, xs ~ (y ': ys)) => proxy xs -> String
test2 prox = test (known prox)
这是
> putStrLn $ test2 (Proxy :: Proxy '["Hello", "Darkness"])
Hello
这是另一个有趣的功能:
data HList :: (k -> *) -> [k] -> * where
HNil :: HList f '[]
HCons :: f a -> HList f as -> HList f (a ': as)
mapC :: (forall a . c a => Proxy a -> f a) -> CList c as -> HList f as
mapC f CNil = HNil
mapC f (CCons (t :: proxy t) ts) = HCons (f (Proxy :: Proxy t)) (mapC f ts)