如何在不与 Haskell 中的类型系统作斗争的情况下对 monad 进行抽象?
How to abstract over monads without fighting the type system in Haskell?
我目前正在 haskell 中构建服务器,作为该语言的新手,我想尝试一种新方法 zu Monad 组合。这个想法是我们可以编写像
这样的库方法
isGetRequest :: (SupportsRequests m r) => m Bool
isGetRequest = do
method <- liftRequests $ requestMethod
return $ method == GET
class (Monad m, RequestSupport r) => SupportsRequests m r | m -> r where
liftRequests :: r a -> m a
class (Monad r) => RequestSupport r where
requestMethod :: r Method
在不知道底层 monad 的情况下工作。当然,在这个例子中,让 isGetRequest 直接在 (RequestSupport r) monad 上运行就足够了,但我的想法是我的库也可能对 monad 有不止一个约束。然而,我不想在同一个模块中实现所有这些不同的关注点,也不想将它们分散到不同的模块(孤儿实例!)。
这就是为什么 m monad 只实现 Supports*
类,将真正的关注委托给其他 monad。
以上代码应该可以完美运行(对 GHC 进行了一些语言扩展)。不幸的是,我在 CRUD(创建读取更新删除)问题上遇到了一些问题:
class (Monad m, CRUDSupport c a) => SupportsCRUD m c a | m a -> c where
liftCRUD :: c x -> m x
class (Monad c) => CRUDSupport c a | c -> a where
list :: c [a] -- List all entities of type a
不,我得到一个错误:
Could not deduce (SupportsCRUD m c a0) from the context [...]
The type variable 'a0' is ambiguous [...]
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
When checking the class method: liftCRUD [...]
似乎类型检查器不喜欢 a
参数没有直接出现在 liftCRUD 的签名中。这是可以理解的,因为 a
不能从函数依赖关系中导出。
我大脑中的类型检查器告诉我,当在库方法中执行某些有关 CRUD 的方法时,稍后使用 AllowAmbiguousTypes 推断类型 a
应该不是问题。不幸的是,GHC 似乎无法执行此推理步骤,例如
bookAvailable :: (SupportsCRUD m c Book) => m Bool
bookAvailable = do
books <- liftCRUD (list :: c [Book]) -- I use ScopedTypeVariables
case books of
[] -> return False
_ -> return True
产量
Could not deduce (SupportsCRUD m c0 a1) arising from a use of 'liftCRUD' [...]
The type variables c0, a1 are ambiguous [...]
看来编译器我还是推理不出来。我有办法解决这个问题吗?或者至少是一种了解编译器能够推断出什么的方法?
此致,
布洛克斯
要使用 ScopedTypeVariables
,您还需要将要在范围内的变量绑定到 forall
。所以应该是
bookAvailable :: forall m c. (SupportsCRUD m c Book) => m Bool
...
这就是让我编译代码所必需的(在我做了一些琐碎的修复之后,我认为这些修复是输入你的问题的拼写错误)。
我目前正在 haskell 中构建服务器,作为该语言的新手,我想尝试一种新方法 zu Monad 组合。这个想法是我们可以编写像
这样的库方法 isGetRequest :: (SupportsRequests m r) => m Bool
isGetRequest = do
method <- liftRequests $ requestMethod
return $ method == GET
class (Monad m, RequestSupport r) => SupportsRequests m r | m -> r where
liftRequests :: r a -> m a
class (Monad r) => RequestSupport r where
requestMethod :: r Method
在不知道底层 monad 的情况下工作。当然,在这个例子中,让 isGetRequest 直接在 (RequestSupport r) monad 上运行就足够了,但我的想法是我的库也可能对 monad 有不止一个约束。然而,我不想在同一个模块中实现所有这些不同的关注点,也不想将它们分散到不同的模块(孤儿实例!)。
这就是为什么 m monad 只实现 Supports*
类,将真正的关注委托给其他 monad。
以上代码应该可以完美运行(对 GHC 进行了一些语言扩展)。不幸的是,我在 CRUD(创建读取更新删除)问题上遇到了一些问题:
class (Monad m, CRUDSupport c a) => SupportsCRUD m c a | m a -> c where
liftCRUD :: c x -> m x
class (Monad c) => CRUDSupport c a | c -> a where
list :: c [a] -- List all entities of type a
不,我得到一个错误:
Could not deduce (SupportsCRUD m c a0) from the context [...]
The type variable 'a0' is ambiguous [...]
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
When checking the class method: liftCRUD [...]
似乎类型检查器不喜欢 a
参数没有直接出现在 liftCRUD 的签名中。这是可以理解的,因为 a
不能从函数依赖关系中导出。
我大脑中的类型检查器告诉我,当在库方法中执行某些有关 CRUD 的方法时,稍后使用 AllowAmbiguousTypes 推断类型 a
应该不是问题。不幸的是,GHC 似乎无法执行此推理步骤,例如
bookAvailable :: (SupportsCRUD m c Book) => m Bool
bookAvailable = do
books <- liftCRUD (list :: c [Book]) -- I use ScopedTypeVariables
case books of
[] -> return False
_ -> return True
产量
Could not deduce (SupportsCRUD m c0 a1) arising from a use of 'liftCRUD' [...]
The type variables c0, a1 are ambiguous [...]
看来编译器我还是推理不出来。我有办法解决这个问题吗?或者至少是一种了解编译器能够推断出什么的方法?
此致, 布洛克斯
要使用 ScopedTypeVariables
,您还需要将要在范围内的变量绑定到 forall
。所以应该是
bookAvailable :: forall m c. (SupportsCRUD m c Book) => m Bool
...
这就是让我编译代码所必需的(在我做了一些琐碎的修复之后,我认为这些修复是输入你的问题的拼写错误)。