Haskell 具有参数类型的类型类

Haskell Typeclasses with parametric types

我想定义一个具体的仿函数如下:

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}

data ValOrError a b = Val a | Error b

class MF c a b where
  mcons :: a -> c -> c
  merr :: b -> c
  mhead :: c -> ValOrError a b
  mtail :: c -> ValOrError c b

我希望类型 c 上的类型类 MF 具有类型参数 ab。我试图在这样的数据结构上定义一个过滤函数,如下所示:

mfilter f e =
  let h = mhead e in
  let t = mtail e in
  case h of
    Error b -> e
    Val a -> case (f a) of
      True -> case t of
        Error d -> mcons a (merr d)
        Val b -> mcons a (mfilter f b)
      False -> case t of
        Error d -> merr d
        Val b -> mfilter f b

但是我得到以下错误:

haskell.hs:24:1:

Could not deduce (MF c a2 b3)
  arising from the ambiguity check for ‘mfilter’
from the context (MF c a5 b6,
                  MF c a4 b5,
                  MF c a4 b4,
                  MF c a4 b,
                  MF c a3 b6,
                  MF c a b6)
  bound by the inferred type for ‘mfilter’:
             (MF c a5 b6, MF c a4 b5, MF c a4 b4,
              MF c a4 b, MF c a3 b6, MF c a b6) =>
             (a4 -> Bool) -> c -> c
  at haskell.hs:(24,1)-(35,28)
The type variables ‘a2’, ‘b3’ are ambiguous
When checking that ‘mfilter’
  has the inferred type ‘forall b c a b1 a1 a2 b2 a3 b3.
                         (MF c a3 b3, MF c a2 b2, MF c a2 b1,
                          MF c a2 b, MF c a1 b3, MF c a b3) =>
                         (a2 -> Bool) -> c -> c’
Probable cause: the inferred type is ambiguous

我想知道 haskell 中是否有更好的方法来声明类型 c 始终具有 ab 作为类型参数。使用类似 Java 的语法:

public interface MF<A,B> {
   MF<A,B> mcons(A head, MF<A,B> tail);
   MF<A,B> merr(B error);
   ValOrError<A,B> head(MF<A,B> e);
   ValOrError<MF<A,B>,B> tail(MF<A,B> e);
}

另外,另一方面,过滤函数应该有一个类型:

mfilter :: (a -> Bool) -> MF c a b -> MF c a b

最直接的方法是从您的代码开始,将 功能依赖项 添加到您的类型 class:

{-# LANGUAGE FunctionalDependencies #-}
class MF c a b | c -> a, c -> b where
  ...

这实际上只是告诉编译器 ab 的类型信息已经包含在 c 中(因此可以在调用站点提取,因此 a2, b3 等不会有歧义)。当您定义 instance MF 时,GHC 可以确定如何准确提取此信息。虽然通常这很有效,但我觉得你为什么要这样做有点疑问:如果 c 总是有 X a b 的形式(并且 X 是一个合适的 data-type-function that can be partially applyed),那为什么还要在class头部提到ab呢?它们基本上是多余的。为什么不给 class 一个参数(属于 Type -> Type -> Type 类型)然后可以 应用 ab

class MF x where
  mcons :: a -> x a b -> x a b
  merr :: b -> x a b
  mhead :: x a b -> ValOrError a b
  mtail :: x a b -> ValOrError (x a b) b

或者,如果你真的想让 c 有种类 Type(这确实有道理!),我应该建议存放 ab 类型在 class 定义中 作为类型族:

{-# LANGUAGE TypeFamilies #-}
class MF c where
  type ValType c :: *
  type ErrType c :: *
  mcons :: ValType c -> c -> c
  merr :: ErrType c -> c
  mhead :: c -> ValOrError (ValType c) (ErrType c)
  mtail :: c -> ValOrError c (ErrType c)

这基本上等同于 TypeFamilies 解决方案,但提供了一个更明确、不那么神秘(尽管也更冗长)的界面。