如何在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,我也这么认为。是否有任何推荐的库,它实际上是以类型级别的方式进行的?我已经对值级别异构集合的实现进行了一些调查。