如何在不与Haskell中的类型系统对抗的情况下对Monad进行抽象?

如何在不与Haskell中的类型系统对抗的情况下对Monad进行抽象?,haskell,typeclass,separation-of-concerns,hindley-milner,Haskell,Typeclass,Separation Of Concerns,Hindley Milner,我目前正在用haskell构建一个服务器,作为该语言的新手,我想尝试一种新的方法来编写Monad。这个想法是我们可以编写库方法,比如 isGetRequest :: (SupportsRequests m r) => m Bool isGetRequest = do method <- liftRequests $ requestMethod return $ method == GET class (Monad m, Req

我目前正在用haskell构建一个服务器,作为该语言的新手,我想尝试一种新的方法来编写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
不,我得到一个错误:

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
看来我还是无法对编译器进行推理。我有办法解决这个问题吗?或者至少是一种理解编译器能够推断出什么的方法

致以最良好的祝愿,
bloxx

要使用
ScopedTypeVariables
,还需要为所有
绑定要在范围内的变量。应该如此

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

这就是让代码编译所需的全部(在我做了一些琐碎的修改之后,我认为这些修改是输入您的问题时的输入错误)。

支持Scrud
类声明涉及三个类型变量,
m
c
a
。你能说出你希望编译器在你的例子中为这三个变量推断出什么值吗?为什么?你的fundep
MA->c
打算做什么?这意味着“对
c
的选择是由特定类型
m
a
唯一确定的”,这意味着类型检查器可以在上下文中仅基于
m
a
选择实例,而
c
遵循您在实例头中输入的任何内容。@jberryman它实际上应该告诉编译器,对于monad m和“CRUD实体”a,类型检查器可以选择唯一的monad c(这是支持CRUD操作的一个)。@DanielWagner的想法是,这里根本没有指定m。m是我们想从bookAvailable取回的单子。一本书应该只是一本书,因为它是我们运行CRUD查询的实体。因为m实现了SupportsCRUD,编译器应该推断出要提升到的monad c。c是支持CRUD操作的monad。我希望这有帮助;我无法将我的问题浓缩成一个更简单的例子。为了重现您引用的错误,我对您的代码进行了一些明显的修复。把它记在这里,这样你就可以复习了,以防你遗漏了什么,而不仅仅是一个打字错误。哇!嗯,这让我大吃一惊,现在我有一个问题:为什么GHC在推断
liftCRUD
的类型时愿意选择
a~Book
?显然,有了这个选择,就有了一个合适的例子;但很明显,一个人可以稍后再出现并添加另一个实例,这也是一个很好的选择,对我来说,这叫“模棱两可”。啊哈,在试图为我的主张作证后,“很明显,一个人可以稍后出现并添加另一个实例”,我想也许我看到了原因:在
CRUDSupport
上有一个键
c->a
fundep,所有
SupportsCRUD
实例都意味着一个对应的
CRUDSupport
实例。@DanielWagner是的,这也让我大吃一惊。我错过了
CRUDSupport
SupportsCRUD
的一个超类,因为它是在之后声明的,我习惯于超类排在第一位。很高兴知道,我仍然不熟悉Haskell的最佳实践约定:)@是的,我也不确定。我看到两种风格:“首先是我想要定义的东西,然后是我需要定义的东西”,和“这里有一些定义,毕竟我们可以得到我们想要的”。不同的思维方式。
Could not deduce (SupportsCRUD m c0 a1) arising from a use of 'liftCRUD' [...]
The type variables c0, a1 are ambiguous [...]
bookAvailable :: forall m c. (SupportsCRUD m c Book) => m Bool
...