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
具有类型参数 a
和 b
。我试图在这样的数据结构上定义一个过滤函数,如下所示:
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
始终具有 a
和 b
作为类型参数。使用类似 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
...
这实际上只是告诉编译器 a
和 b
的类型信息已经包含在 c
中(因此可以在调用站点提取,因此 a2
, b3
等不会有歧义)。当您定义 instance MF
时,GHC 可以确定如何准确提取此信息。虽然通常这很有效,但我觉得你为什么要这样做有点疑问:如果 c
总是有 X a b
的形式(并且 X
是一个合适的 data
-type-function that can be partially applyed),那为什么还要在class头部提到a
和b
呢?它们基本上是多余的。为什么不给 class 一个参数(属于 Type -> Type -> Type
类型)然后可以 应用 到 a
和 b
?
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
(这确实有道理!),我应该建议存放 a
和 b
类型在 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
解决方案,但提供了一个更明确、不那么神秘(尽管也更冗长)的界面。
我想定义一个具体的仿函数如下:
{-# 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
具有类型参数 a
和 b
。我试图在这样的数据结构上定义一个过滤函数,如下所示:
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
始终具有 a
和 b
作为类型参数。使用类似 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
...
这实际上只是告诉编译器 a
和 b
的类型信息已经包含在 c
中(因此可以在调用站点提取,因此 a2
, b3
等不会有歧义)。当您定义 instance MF
时,GHC 可以确定如何准确提取此信息。虽然通常这很有效,但我觉得你为什么要这样做有点疑问:如果 c
总是有 X a b
的形式(并且 X
是一个合适的 data
-type-function that can be partially applyed),那为什么还要在class头部提到a
和b
呢?它们基本上是多余的。为什么不给 class 一个参数(属于 Type -> Type -> Type
类型)然后可以 应用 到 a
和 b
?
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
(这确实有道理!),我应该建议存放 a
和 b
类型在 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
解决方案,但提供了一个更明确、不那么神秘(尽管也更冗长)的界面。