haskell - 如何解决“无法将类型 'b' 与 ConcreteType 匹配”?
问题描述
架构如下:用户生成一个 Intent,然后由系统捕获。Resolver 会找到一个合适的函数来解决这个意图。有一个索引将意图映射到在这些意图中提到其功能的模块。基本思想是模块接受一个实体并生成另一个实体作为结果。
我尝试了 ExistentialTypes,但我认为我还没有足够的专业知识来使用它们,所以我想知道是否有更简单的方法。
一个实体看起来像这样。
data Entity a = Entity {...}
data DirectionEntity = DirectionEntity {...}
有很多实体。
处理程序如下所示:
handler :: Entity NavigationEntity -> IO (Entity DirectionEntity)
我想在类型级别获得一些信息。一切都很好,直到我想在一个地方拥有一个包含所有这些处理程序的单一数据结构。基本上,我想要一个类似的功能:
solveIntent :: Intent -> Entity a -> IO (Entity b)
solveIntent intent entity = do
index <- mkIndex
let m = searchModule index intent
run m entity
这是标题中的问题:我无法匹配类型:
Expected type: Entity a -> IO (Entity b)
Actual type: Entity NavigationEntity -> IO (Entity DirectionEntity)
任何帮助,将不胜感激。谢谢。
解决方案
像这样的数据类型Intent
不公开类型信息。for的类型与Intent
forNavigationEntity -> DirectionEntity
的类型相同。因此,结果的类型不能基于 改变,你就有问题了,因为这就是重点。Intent
DirectionEntity -> NavigationEntity
searchModule
Intent
退后一步,你有大量的exists a b. Entity a -> IO (Entity b)
s 集合。您希望能够选择两个Type
s — <code>a 和b
— 并在您的集合中搜索匹配的函数。这是一项工作Typeable
:您需要定义一个类型来保存 each exists a b. Entity a -> IO (Entity b)
,加上and的Typeable
证据,然后应该为您正在搜索的两种类型获取证据。你不需要一个或任何东西。您只需要您正在搜索的模块的类型。要处理的最简单的集合类型是 just 。a
b
lookupModule
Typeable
Intent
[]
import Data.Type.Equality
import Type.Reflection
data DirectionEntity deriving Typeable -- needs DeriveDataTypeable extension
data NavigationEntity deriving Typeable
data SomeModule =
-- needs ExistentialQuantification, ExplicitForAll extensions
forall a b. (Typeable a, Typeable b) =>
SomeModule (Entity a -> IO (Entity b))
type ModuleIndex = [SomeModule]
handler :: Entity NavigationEntity -> IO (Entity DirectionEntity)
index :: ModuleIndex
index = [SomeModule handler] -- or whatever
-- really, this is more of a mapMaybe/filter than a lookup
lookupModule ::
forall a b. (Typeable a, Typeable b) => -- needs ScopedTypeVariables
ModuleIndex -> [Entity a -> IO (Entity b)] -- can have any number of matches!
lookupModule [] = []
-- we have a, b, which are our search queries
-- we extract x, y from the `SomeModule`
-- we have Typeable for all four, so we check for a match
-- then decide whether or not to include the function
-- (well, *we* don't really decide; without a match, it's a type error!)
lookupModule (SomeModule (x :: Entity x -> IO (Entity y)) : xs)
-- needs GADTs, TypeOperators extensions
| Just Refl <- testEquality typeRep typeRep :: Maybe (a :~: x)
, Just Refl <- testEquality typeRep typeRep :: Maybe (b :~: y)
= x : lookupModule xs
| otherwise = lookupModule xs
哪个应该给你类似的东西
> length (lookupModule index :: [Entity NavigationEntity -> IO (Entity DirectionEntity)])
1
> length (lookupModule index :: [Entity DirectionEntity -> IO (Entity NavigationEntity)])
0
如果您的收藏真的很大,您可能需要使用Map
. 这变得更加棘手,因为我们使用了不安全的操作,但接口仍然是安全的。这应该有自己的模块,用于访问控制。
module ModuleIndex(ModuleIndex, SomeModule(..), fromList, lookupModule) where
import Type.Reflection
import Unsafe.Coerce
import Data.Map as M
data Key = forall (a :: Type) (b :: Type). Key (TypeRep a) (TypeRep b)
instance Eq Key where
Key a b == Key x y =
SomeTypeRep a == SomeTypeRep x && SomeTypeRep b == SomeTypeRep y
instance Ord Key where
compare (Key a b) (Key x y) =
compare (SomeTypeRep a) (SomeTypeRep x) <> compare (SomeTypeRep b) (SomeTypeRep y)
data Value = forall a b. Value (Entity a -> IO (Entity b))
newtype ModuleIndex = ModuleIndex { getModuleIndex :: Map Key Value }
data SomeModule =
forall a b. (Typeable a, Typeable b) =>
SomeModule (Entity a -> IO (Entity b))
fromList :: [SomeModule] -> ModuleIndex
fromList = ModuleIndex . M.fromList . fmap disentangle
where disentangle (SomeModule (f :: Entity a -> IO (Entity b))) =
(Key (typeRep :: TypeRep a) (typeRep :: TypeRep b), Value f)
lookupModule ::
forall a b. (Typeable a, Typeable b) =>
ModuleIndex -> Maybe (Entity a -> IO (Entity b))
lookupModule = fmap extract . M.lookup key . getModuleIndex
where key = Key (typeRep :: TypeRep a) (typeRep :: TypeRep b)
extract (Value f) = unsafeCoerce f :: Entity a -> IO (Entity b)
推荐阅读
- mongodb - 无法连接MongoDB
- android - 在没有root的情况下禁用无线Android(ADB)调试
- node.js - 如何在 Ubuntu 16.04 服务器中通过源代码安装 Node.Js?
- python - Python 使用键排序:可以并行化吗?
- javascript - 如何在编译时使用 webpack 替换文件?
- batch-file - 将递增计数器连接到批处理文件中的字符串变量
- reference - 如何将 Option<&T> 转换为 Option
以 Rust 中最惯用的方式? - python - PyQtChart 不显示数据
- java - string.equalsIgnoreCase(object.string) 与 object.string.equalsIgnoreCase(string) 之间的区别
- mongodb - MongoDB,数组,子元素,计数,百分比