【问题标题】:Constructing Proxy type given the input给定输入构造代理类型
【发布时间】:2016-04-02 16:10:23
【问题描述】:

鉴于下面的代码在 Data.HashMap 中查找类型的特定信息,是否可以定义一个新函数 getMapVal2,如 cmets 中所述,以在给定类型的情况下构建 TypeKey 参数?

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DataKinds #-}

import Data.Monoid ((<>))
import Data.Proxy (Proxy(Proxy))
import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)
import qualified Data.HashMap.Strict as Map (HashMap, empty, insert, lookup)
import Data.Dynamic
import GHC.Generics
import Data.Maybe (fromJust, isNothing, maybe)

type family TypeKey (a :: *) :: Symbol where
    TypeKey Int = "int"
    TypeKey T = "trec"

data T = T { aInt :: Int} deriving (Show, Generic, Typeable)

extract ::(s ~ TypeKey a, Typeable a, KnownSymbol s) => Maybe Dynamic -> Maybe a
extract dyn = if (isNothing dyn) then Nothing else fromDynamic . fromJust $ dyn   

getMapVal :: (s ~ TypeKey a, Typeable a, KnownSymbol s) => Map.HashMap String Dynamic -> String -> Maybe a
getMapVal m k = extract $ Map.lookup k m 

{-- How do we get the TypeKey lookup for type a?
getMapVal2 :: (s ~ TypeKey a, Typeable a, KnownSymbol s) => Map.HashMap String Dynamic -> a -> Maybe a
getMapVal2 m ty = extract $ Map.lookup (symbolVal (Proxy :: Proxy (TypeKey ???))) m
--}

main = do
  let map = Map.insert (symbolVal (Proxy :: Proxy (TypeKey T))) (toDyn $ T {aInt=5}) Map.empty -- we insert some value in hashmap for type T - it is of same type
      val = getMapVal map (symbolVal (Proxy :: Proxy (TypeKey T))) :: Maybe T -- now let us retrieve the value in map for Type T. We pass the SymbolVal ourselves
    --val =  getMapVal2 map (T {aInt = 2}) -- now we want to lookup map value given something of a type T. Need getMapVal2 to build symbolval given the input type
  print $ maybe "" show val -- prints value stored in Hashmap for type T which is: T {aInt=5}

这只是一个玩具代码,用于测试在运行时通过 Data.HashMap 将特定于类型的配置传递给作用于类型类类型的多态函数。

【问题讨论】:

  • 顺便说一句,你想在这里实现什么?我注意到一些代码异味:动态类型、“字符串类型”类型级计算、non-covering closed type families。我想知道如果您改为围绕单例值重新组织代码,您的生活是否会更轻松。
  • @BenjaminHodgson,是的,我不喜欢这种方法,但这是我要解决的问题:给定一组不同类型的数据构造函数,在该集合上定义一个多态函数,调用特定类型的构建器(某种 XML 模板 - a 类型的数据被转换为 a 类型的 XML 请求)。为此,我需要在运行时配置加载构建器,并传递给函数,以便它可以在给定a 类型的数据的情况下检索a 类型的构建器,然后调用它。该功能与main 相差几个功能。
  • @BenjaminHodgson,我也考虑过reification approach(基本上,用builder和builder函数定义一个typeclass,并在运行时传递builder。但是,我脑子里想出的代码并没有看起来也不干净。也许物化是一种更好的方法,我应该在 SO 提出另一个问题。
  • 我担心您可能会将命令式(/OO(/Java)) 风格的思维带入函数式环境。对我来说,这听起来像是一个教科书用例:class ToXml a where { toXml :: a -&gt; Xml }。不需要像这样或reflection这样复杂的代码。
  • @BenjaminHodgson,但是,toXML 本身是从文件运行时加载,而不是编译时。万一它让你放心,我很长时间没有用命令式语言编程了:) 只是这个问题本身似乎是命令式的——主要是,我们这样做(实际上这是一个heist 编译的构建器) :aFn :: StateT AnAPI IO Builder &lt;- genBuilder "curdir" "atemplate"。使用运行时 aFn 成员来实例化编译时类可能是不可能的。

标签: haskell data-kinds


【解决方案1】:

使用the ScopedTypeVariables extension。这允许您在绑定它们的定义主体中引用forall-bound 类型变量。

{-# LANGUAGE ScopedTypeVariables #-}

getMapVal2 :: forall a s. (s ~ TypeKey a, Typeable a, KnownSymbol s) => Map.HashMap String Dynamic -> a -> Maybe a
getMapVal2 m ty = extract $ Map.lookup (symbolVal (Proxy :: Proxy (TypeKey a))) m

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多