Dictionary 如何(不安全地)将贴图反映为约束?

Dictionary 如何(不安全地)将贴图反映为约束?,dictionary,haskell,reflection,typeclass,singleton-type,Dictionary,Haskell,Reflection,Typeclass,Singleton Type,我想要一个typeclass,它表示具体化的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

我想要一个typeclass,它表示具体化的
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

为什么它必须是类
IsMember
?简单的类型如何:

newtype Member x s v = Member v

checkMember :: ... => proxy x -> Maybe (Member x s v)
保留
Member
摘要允许保留类型为
Member x s v
的值属于与
s
关联的字典的不变量。您不需要
不安全措施

从那里,可能还有一些方法可以使用反射将
成员提升回类型级别,但这听起来太过工程化了


编辑:从讨论中可以看出,该需求似乎是外部的,没有什么可做的。下面是一种实现
checkMember
的方法

reflection
也实现了它自己的机制。)

我们可以滥用这样一个事实,即GHC使用一个方法而没有超类
类C a,其中m::v
直接到未包装的方法
m::v
,并将值
CA=>b
约束到函数
v->b

  • 我们需要一个没有超类的
    IsMember
    版本(
    IsMember0
  • 我们将
    ismember0xsv=>r
    包装成一个新类型,这样它就可以被强制为
    ismember0xsv->r
    UnsafeMember

{-#语言约束种类}
{-#语言数据类型}
{-#语言灵活语境#-}
{-#语言灵活实例}
{-#语言功能依赖性}
{-#语言签名{-}
{-#语言MultiParamTypeClasses}
{-#语言等级}
{-#语言范围类型变量#-}
{-#语言类型应用程序{-}
{-#语言不可判定实例}
导入数据.约束(Dict(..)
导入数据.Map(Map)
导入符合条件的数据。映射为映射
导入数据代理(代理(..)
导入数据。反射(具体化、反映、具体化)
导入GHC.TypeLits(已知符号、符号、符号值)
导入不安全。强制(不安全强制)
键入文本=字符串
类IsMember0(x::Symbol)s v | s->v其中
值0::代理s->代理x->v
类(具体化s(映射文本v),IsMember0 x sv)=>IsMember(x::Symbol)sv | s->v
实例(具体化s(映射文本v),IsMember0 x s v)=>IsMember(x::Symbol)s v
值::IsMember x s v=>Proxy s->Proxy x->v
值=值0
newtype UnsafeMember x s v=UnsafeMember(IsMember0 x s v=>Dict(IsMember x s v))
非女性成员::对于所有x s v。具体化s(映射文本v)=>v->Dict(IsMember x s v)
未安全成员v=未安全成员(未安全成员@x@s@v Dict)(\ \uuuu->v)
checkMember::forall s x v proxys proxys。(已知符号x,具体化s(地图文本v))
=>proxys->proxyx->Maybe(Dict(IsMember x s v))
检查成员usx=
设m=reflect(Proxy@s)
在unsafeMember Map.lookup(symbolVal sx)m中
--可执行示例
main::IO()
main=do
设d=Map.fromList[(“foo”,33::Int)]
foo=代理::代理“foo”
具体化d(\p->
案例检查成员p foo
无->失败“未找到”
只需口述->打印(值0 p foo))

它必须是一个类,因为我将它用作其他地方定义的另一个类的实例的约束,并且我无法对其进行更改。这与
反射
首先存在的原因相同。因为有时我们需要将参数输入类而不是方法。@dspyz也许您可以使用
someSymbolVal
将术语级字符串转换为
SomeSymbol
,然后对结果进行模式匹配
newtype Member x s v = Member v

checkMember :: ... => proxy x -> Maybe (Member x s v)
{-# 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))