如何在Haskell中使用类型级键编译异构散列映射类对象?

如何在Haskell中使用类型级键编译异构散列映射类对象?,haskell,type-level-computation,Haskell,Type Level Computation,我想使用类似“使用类型级键的异构哈希映射”的东西。 在苦苦挣扎之后,我写了一段代码(尽管事实上,它只是一个类似列表的结构,而不是散列图),但并没有成功地编译 错误消息如下所示 • Couldn't match expected type ‘ValueType (Proxy k') (NamedVal v k :. b)’ with actual type ‘ValueType (Proxy k') b

我想使用类似“使用类型级键的异构哈希映射”的东西。 在苦苦挣扎之后,我写了一段代码(尽管事实上,它只是一个类似列表的结构,而不是散列图),但并没有成功地编译

错误消息如下所示

• Couldn't match expected type ‘ValueType
                                  (Proxy k') (NamedVal v k :. b)’
              with actual type ‘ValueType (Proxy k') b’
  NB: ‘ValueType’ is a type function, and may not be injective
• In the expression: get (Proxy :: Proxy k') b
似乎编译器并不像我所说的那个样理解类型族声明。 如何编译此代码

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE IncoherentInstances #-}

module TypeLevelKV where

{- | The goal of this module is to get the value with a type level key.

>>> get (Proxy :: Proxy "foo") sampleList
"str"

>>> get (Proxy :: Proxy "bar") sampleList
34
-}

import Data.Proxy (Proxy(..))
import Data.Typeable (Typeable)
import GHC.TypeLits (KnownSymbol, symbolVal)

sampleList =
  (namedVal "str"   :: NamedVal String "foo") :.
  (namedVal 34      :: NamedVal Int "bar") :.
  (namedVal True    :: NamedVal Bool "baz") :. Null

{- | A value with type level index.
 -}
type NamedVal v key = (Proxy key, v)

{- | Type level list cons
 -}
data a :. b = a :. b
    deriving (Typeable, Eq, Show)
infixr 8 :.

{- | Type level @[]@
 -}
data Null = Null
    deriving (Typeable, Eq, Show)

type family ValueType pkey list where
  ValueType (Proxy k) (NamedVal v k :. b) = v
  ValueType (Proxy k') (NamedVal v k :. b) = ValueType (Proxy k') b
  ValueType (Proxy k) Null = Null

class HasKey pkey list where
  get :: pkey -> list -> (ValueType pkey list)

instance (KnownSymbol k) =>
  HasKey (Proxy k) (NamedVal v k :. b) where
  get _ ((_, a) :. _) = a

instance (KnownSymbol k, KnownSymbol k', HasKey (Proxy k') b) =>
  HasKey (Proxy k') (NamedVal v k :. b) where
  get _ (_ :. b) = get (Proxy :: Proxy k') b

instance (KnownSymbol k) =>
  HasKey (Proxy k) Null where
  get _ Null = Null

namedVal :: forall k v
         . (KnownSymbol k)
         => v -> NamedVal v k
namedVal a = (Proxy, a)
以下是对@Benjamin Hodgson评论的回复 我需要它的原因之一是,我希望将它与模板haskell一起使用,如下所示,并确保
sampleQ
所需的所有变量都是所需的相同类型

withDict :: [String] -> Q Exp -> Q Exp
withDict keys q = undefined
-- e.g., `withDict (Proxy :: Proxy MyType) sampleQ` generates
-- \(dict :: MyType) ->
--   let
--     foo = get (Proxy :: Proxy "foo") dict
--     bar = get (Proxy :: Proxy "bar") dict
--     baz = get (Proxy :: Proxy "baz") dict
--     defaultValue = "If no \"defaultValue\" key exists, this default value is provided to sampleQ"
--   in
--     $(sampleQ)

type MyType =
  NamedVal String "foo" :.
  NamedVal Int "bar" :.
  NamedVal Bool "baz" :. Null
定义这个之后,我们可以将任何变量名的值注入
qexp
,比如莎士比亚模板

renderWith :: IO ()
renderWith = do
  foo <- getLine
  bar <- readLn
  baz <- readLn
  let
    sampleList :: MyType
    sampleList =
      (namedVal foo   :: NamedVal String "foo") :.
      (namedVal bar   :: NamedVal Int "bar") :.
      (namedVal baz   :: NamedVal Bool "baz") :. Null
  putStrLn $ $(withDict (Proxy :: Proxy MyType) renderSomeFile) sampleList
renderWith::IO()
renderWith=do

foo我发现下面的代码运行良好,并使用此代码发布了模块

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE IncoherentInstances #-}

module Type where

import Data.Proxy (Proxy(..))
import Data.Typeable (Typeable)
import GHC.TypeLits (KnownSymbol, symbolVal)

{- | A value with type level index.
 -}
type NamedVal v key = (Proxy key, v)

{- | Type level list cons
 -}
data a :. b = a :. b
    deriving (Typeable, Eq, Show)
infixr 8 :.

{- | Type level @[]@
 -}
data Null = Null
    deriving (Typeable, Eq, Show)

type family Lookup pkey list where
  Lookup pk ((pk, v) :. b) = v
  Lookup pk ((px, v) :. b) = Lookup pk b
  Lookup pk Null = Null

class HasKey list pkey value where
  get' :: pkey -> list -> value

instance (KnownSymbol k) =>
  HasKey (NamedVal v k :. b) (Proxy k) v where
  get' :: forall b0 k0 v0
      .  (KnownSymbol k0)
      => Proxy k0 -> NamedVal v0 k0 :. b0 -> v0
  get' _ ((_, a) :. _) = a

instance (NamedList b, KnownSymbol k, HasKey b (Proxy k) v) =>
  HasKey (a :. b) (Proxy k) v where
  get' _ (_ :. b) = get' (Proxy :: Proxy k) b

instance (KnownSymbol k, v ~ Null) =>
  HasKey Null (Proxy k) v where
  get' _ Null = Null

get :: (HasKey list pkey (Lookup pkey list))
    => pkey -> list -> Lookup pkey list
get (pkey :: pkey) (list :: list) =
  get' pkey list :: Lookup pkey list

我发现下面的代码运行良好,并使用此代码发布了模块

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE IncoherentInstances #-}

module Type where

import Data.Proxy (Proxy(..))
import Data.Typeable (Typeable)
import GHC.TypeLits (KnownSymbol, symbolVal)

{- | A value with type level index.
 -}
type NamedVal v key = (Proxy key, v)

{- | Type level list cons
 -}
data a :. b = a :. b
    deriving (Typeable, Eq, Show)
infixr 8 :.

{- | Type level @[]@
 -}
data Null = Null
    deriving (Typeable, Eq, Show)

type family Lookup pkey list where
  Lookup pk ((pk, v) :. b) = v
  Lookup pk ((px, v) :. b) = Lookup pk b
  Lookup pk Null = Null

class HasKey list pkey value where
  get' :: pkey -> list -> value

instance (KnownSymbol k) =>
  HasKey (NamedVal v k :. b) (Proxy k) v where
  get' :: forall b0 k0 v0
      .  (KnownSymbol k0)
      => Proxy k0 -> NamedVal v0 k0 :. b0 -> v0
  get' _ ((_, a) :. _) = a

instance (NamedList b, KnownSymbol k, HasKey b (Proxy k) v) =>
  HasKey (a :. b) (Proxy k) v where
  get' _ (_ :. b) = get' (Proxy :: Proxy k) b

instance (KnownSymbol k, v ~ Null) =>
  HasKey Null (Proxy k) v where
  get' _ Null = Null

get :: (HasKey list pkey (Lookup pkey list))
    => pkey -> list -> Lookup pkey list
get (pkey :: pkey) (list :: list) =
  get' pkey list :: Lookup pkey list

这段代码看起来太复杂了。你到底想干什么?为什么要使用“具有类型级别键的哈希”?谢谢您的评论。我在问题中添加了我的动机。实例
HasKey(Proxy k')(NamedVal v k:.b)
不知道
k
k'
是不同的类型,因此它不知道
ValueType
的第一个模式不匹配。有几个库实现了异构集合-您应该从这些库开始进行检查。@user2407038,我也这么认为。是否有任何推荐的库,它实际上是以类型级别的方式进行的?我已经对值级异构集合的实现做了一些调查。这段代码看起来太复杂了。你到底想干什么?为什么要使用“具有类型级别键的哈希”?谢谢您的评论。我在问题中添加了我的动机。实例
HasKey(Proxy k')(NamedVal v k:.b)
不知道
k
k'
是不同的类型,因此它不知道
ValueType
的第一个模式不匹配。有几个库实现了异构集合-您应该从这些库开始进行检查。@user2407038,我也这么认为。是否有任何推荐的库,它实际上是以类型级别的方式进行的?我已经对值级别异构集合的实现进行了一些调查。