dictionary - 如何(不安全地)将 map 反射(reflect)为约束?

标签 dictionary haskell reflection typeclass singleton-type

我想要一个类型类来表示实体化 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/

    相关文章:

    python - 在Python中合并两个字典,以便添加/减去它们的键

    haskell - Docker + Yesod链接失败

    Haskell - 如何编写惯用且高效的循环?

    Go lang反射如何识别接口(interface)底层类型

    c# - 有没有办法通过反射设置 C# 只读自动实现的属性?

    c++ - STL 中的抽象 map 和 vector

    java - 在这种情况下,TreeBasedTable.create().rowMap().get(rowKey) 将返回一个空映射

    python - 如何将 JSON 转储从表单填充到 Django 模板?

    haskell - 异构列表的显示实例

    vb.net - 在 VB.NET 中跟踪所有事件