我想要一个类型类来表示实体化 Data.Map.Map
中的成员资格.所以像:
class Reifies s (Map Text v) => IsMember (x :: Symbol) s where
value :: Proxy s -> Proxy x -> v
然后我想实现一个返回
Dict
的函数每当出现符号时,此类的实例:checkMember :: forall s x v. (KnownSymbol x, Reifies s (Map Text v))
=> proxy x -> Maybe (Dict (IsMember x s))
checkMember sx =
let m = reflect (Proxy @s)
in (_ :: v -> Dict (IsMember x s)) <$> Map.lookup (symbolVal sx) m
我不介意使用
unsafeCoerce
实现 checkMember
,但即便如此,我还是无法弄清楚如何做到这一点(填写类型孔)。大概的序言:
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
import Data.Constraint(Dict(..))
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Proxy (Proxy(..))
import Data.Reflection (Reifies, reflect)
import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)
最佳答案
为什么它必须是一个类IsMember
?一个简单的类型怎么样:
newtype Member x s v = Member v
checkMember :: ... => proxy x -> Maybe (Member x s v)
饲养
Member
abstract 允许保留类型为 Member x s v
的值的不变量。属于与 s
相关的字典.否 unsafeCoerce
在你的部分是需要的。从那里,可能还有一些方法可以使用反射来提升
Member
回到类型级别,但这听起来过度设计。编辑:从讨论中,似乎要求是外部的,没有什么可做的。这是实现
checkMember
的方法.(
reflection
也像这样实现了自己的机器。)我们可以滥用 GHC 使用单一方法对类进行脱糖而没有父类(super class)的事实
class C a where m :: v
直接到解包方法 m :: v
, 和约束值 C a => b
功能 v -> b
.IsMember
没有父类(super class) ( IsMember0
) IsMember0 x s v => r
在一个 newtype 中,所以它可以被强制为 IsMember0 x s v -> r
( UnsafeMember
) {-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
import Data.Constraint(Dict(..))
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Proxy (Proxy(..))
import Data.Reflection (Reifies, reflect, reify)
import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)
import Unsafe.Coerce (unsafeCoerce)
type Text = String
class IsMember0 (x :: Symbol) s v | s -> v where
value0 :: Proxy s -> Proxy x -> v
class (Reifies s (Map Text v), IsMember0 x s v) => IsMember (x :: Symbol) s v | s -> v
instance (Reifies s (Map Text v), IsMember0 x s v) => IsMember (x :: Symbol) s v
value :: IsMember x s v => Proxy s -> Proxy x -> v
value = value0
newtype UnsafeMember x s v = UnsafeMember (IsMember0 x s v => Dict (IsMember x s v))
unsafeMember :: forall x s v. Reifies s (Map Text v) => v -> Dict (IsMember x s v)
unsafeMember v = unsafeCoerce (UnsafeMember @x @s @v Dict) (\ _ _ -> v)
checkMember :: forall s x v proxys proxyx. (KnownSymbol x, Reifies s (Map Text v))
=> proxys s -> proxyx x -> Maybe (Dict (IsMember x s v))
checkMember _ sx =
let m = reflect (Proxy @s)
in unsafeMember <$> Map.lookup (symbolVal sx) m
-- Executable example
main :: IO ()
main = do
let d = Map.fromList [("foo", 33 :: Int)]
foo = Proxy :: Proxy "foo"
reify d (\p ->
case checkMember p foo of
Nothing -> fail "Not found"
Just Dict -> print (value0 p foo))
关于dictionary - 如何(不安全地)将 map 反射(reflect)为约束?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53822949/