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)