Haskell 如何绕开;不能';t匹配类型';b';用“混凝土类型”吗;?
该体系结构如下:用户生成意图,然后由系统捕获。解析程序会找到一个适当的函数来解析此意图。有一个索引将意图映射到在这些意图中提到其功能的模块。其基本思想是,模块获取一个实体并生成另一个实体 我尝试过存在主义类型,但我认为我还没有足够的专业知识来处理它们,所以我想知道是否有更简单的方法 一个实体看起来像这样Haskell 如何绕开;不能';t匹配类型';b';用“混凝土类型”吗;?,haskell,Haskell,该体系结构如下:用户生成意图,然后由系统捕获。解析程序会找到一个适当的函数来解析此意图。有一个索引将意图映射到在这些意图中提到其功能的模块。其基本思想是,模块获取一个实体并生成另一个实体 我尝试过存在主义类型,但我认为我还没有足够的专业知识来处理它们,所以我想知道是否有更简单的方法 一个实体看起来像这样 数据实体a=实体{…} 数据方向性=方向性{…} 有很多实体 处理程序如下所示: 处理程序::实体导航实体->IO(实体目录实体) 我想了解一些类型级别的信息。 一切都很好,直到我想要在一个
数据实体a=实体{…}
数据方向性=方向性{…}
有很多实体
处理程序如下所示:
处理程序::实体导航实体->IO(实体目录实体)
我想了解一些类型级别的信息。
一切都很好,直到我想要在一个地方有一个包含所有这些处理程序的单一数据结构。基本上,我希望有一个如下的函数:
solveIntent::Intent->实体a->IO(实体b)
solveIntent实体=do
索引IO(实体b)
实际类型:实体导航实体->IO(实体目录实体)
任何帮助都将不胜感激。谢谢。像
Intent
这样的数据类型不公开类型信息。DirectionEntity->DirectionEntity
的Intent
类型与DirectionEntity->DirectionEntity
的Intent
类型相同。因此,searchModule
的结果类型不能根据意图改变,这就是问题所在
退一步说,你有一个巨大的集合存在着一个b。实体a->IO(实体b)
s。您希望能够选择两个类型
s-a
和b
——并在您的收藏中搜索匹配的功能。这是可键入的作业:您需要定义一个类型来保存每个存在的a b。实体a->IO(实体b)
,加上a
和b
的Typeable
证据,然后lookupModule
应该为您正在搜索的两种类型获取Typeable
证据。你不需要一个意图
或任何东西。您只需要您正在搜索的模块的类型。要处理的最简单的集合类型就是[]
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
如果您的收藏量很大,您可能需要使用地图
。这变得更加棘手,因为我们使用的是不安全的操作,但接口仍然是安全的。这应该有自己的模块,用于访问控制
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)
ModuleIndex(ModuleIndex,SomeModule(..),fromList,lookupModule),其中
导入类型。反射
导入不安全。强制
导入数据。映射为M
数据键=forall(a::Type)(b::Type)。钥匙(TypeRep a)(TypeRep b)
实例Eq键在哪里
键a b==键x y=
SomeTypeRep a==SomeTypeRep x&&SomeTypeRep b==SomeTypeRep y
实例Ord键在哪里
比较(图例a和b)(图例x和y)=
比较(SomeTypeRep a)(SomeTypeRep x)比较(SomeTypeRep b)(SomeTypeRep y)
数据值=对于所有a b。值(实体a->IO(实体b))
newtype ModuleIndex=ModuleIndex{getModuleIndex::Map Key Value}
数据模块=
全部a b。(可打印a、可打印b)=>
SomeModule(实体a->IO(实体b))
fromList::[SomeModule]->ModuleIndex
fromList=模块索引。M.fromList。fmap解开
其中解纠缠(SomeModule(f::实体a->IO(实体b)))=
(键(typeRep::typeRep a)(typeRep::typeRep b),值f)
查找模块::
全部a b。(可打印a、可打印b)=>
模块索引->可能(实体a->IO(实体b))
lookupModule=fmap提取。查找键。getModuleIndex
其中key=key(typeRep::typeRep a)(typeRep::typeRep b)
提取(值f)=未安全提取f::实体a->IO(实体b)
注意,a
和b
是普遍量化的。因此,如果您编写solveIntent::Intent->Entity a->IO(Entity b)
,则表示该函数适用于所有a
s和b
s。但事实并非如此。由于您可能会使用searchModule
等特定于NavigationEntity
的函数。我知道我可以将每个处理程序设置为实体a->IO(实体b)
类型。但这意味着我会丢失类型信息。@dciug您只会丢失不存在的类型信息。。。?b
在您的类型签名中根本不正确。Intent->Entity a->IO(Entity b)
并不意味着solveIntent
可以处理任何实体;它承诺它可以处理每个实体。你能添加完整的代码吗?从数据
和函数中?我已经查阅了几个小时的文档来理解这一点,我学到了很多。这是可行的,但我之所以考虑意向,是因为系统链接到一个NLP模块,该模块从用户的文本请求中提取意向。目前,我必须手动指定lookupModule
函数的结果类型。我可以在运行时自动推断吗?类似于:获取该变量的typeOf
和该变量的typeOf
,然后查找索引。类型信息(Typeable
)不附加到变量。类型信息附加到类型。拥有一个变量x::a
,和拥有类型信息可键入a
是完全不相关的。对于您的用例,我可能会尝试像datasomeentity=forall a这样的方法。可键入a=>SomeEntity(实体a);对于所有a,具有某个实体r=的数据。可键入a=>带有某个实体(实体a->r);handleInput::UserInput->(SomeEntity,带有SomeEntity结果)
,但我需要更多的细节来确定。
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)