如何在不与Haskell中的类型系统作战的情况下抽象monad?

时间:2017-10-10 17:09:55

标签: haskell typeclass separation-of-concerns hindley-milner

我目前正在使用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 [...]

似乎我仍无法推断编译器。我有办法解决这个问题吗?或者至少是了解编译器能够推断出什么的方法?

最诚挚的问候, bloxx

1 个答案:

答案 0 :(得分:3)

要使用ScopedTypeVariables,您还需要使用forall绑定要包含在范围内的变量。所以它应该是

bookAvailable :: forall m c. (SupportsCRUD m c Book) => m Bool
...

这就是所有必要的(在我做了一些微不足道的修复之后,我认为是输入你的问题的错别字)让我得到编译的代码。