具有约束参数功能的类型类

Posted

技术标签:

【中文标题】具有约束参数功能的类型类【英文标题】:typeclass with constrained params' function 【发布时间】:2019-09-23 11:55:32 【问题描述】:

我想写一个这样的类:

class C c where
    op :: c -> c -> Bool

class A b => B b where
    func :: C c => b -> c -- ^ type 'c' is random(forall). 
    func2 :: b -> b -> Bool
    func2 x y = func b `op` func c

这里,c 是一个受C 限制的类型,这个限制将在 func2 中使用。 但这不能是编译器。 c 类型不是真正的类型。我尝试添加forall 或使用TypeFamilies,但他们都无法做到这一点。 TypeFamilies 看起来不错,但它不能在函数定义中使用 with 限制,例如 C c => b -> c 或 `type X x :: C * => *。

我必须使用(A b, C c) => B b c 来定义这个类吗?我有另一个类与 B 一起使用,例如 B b => D d b。如果为 B 类添加一个参数,则 D 类还需要一个参数。其实Seq a会和D类一起使用,不能匹配D d b

编辑:再描述一个。

-# LANGUAGE MultiParamTypeClasses #-
-# LANGUAGE FlexibleInstances #-
module Main where

type Ta = (Integer, Integer)
newtype Tb t = Tb  tb :: [t]  deriving Show

class Eq a => A a where
    a1f :: Ord b => a -> b
    a2f :: a -> a -> Bool
    a2f x y = a1f x >= a1f y

instance A Ta where
    a1f (_, y) = y

class A a => B b a where
    op :: b a -> b a

instance B Tb Ta where
    op x = x

main :: IO ()
main = putStrLn $ show $ op $ (Tb [(1, 1)] :: Tb Ta)

编译器会抱怨a2f :: b -> Bool

    • Could not deduce (Ord a0) arising from a use of ‘>=’
      from the context: A a
        bound by the class declaration for ‘A’ at test.hs:10:15
      The type variable ‘a0’ is ambiguous
      These potential instances exist:
        instance Ord Ordering -- Defined in ‘GHC.Classes’
        instance Ord Integer
          -- Defined in ‘integer-gmp-1.0.2.0:GHC.Integer.Type’
        instance Ord a => Ord (Maybe a) -- Defined in ‘GHC.Maybe’
        ...plus 22 others
        ...plus four instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the expression: a1f x >= a1f y
      In an equation for ‘a2f’: a2f x y = a1f x >= a1f y

EDIT2:使用类型族

...
class Eq a => A a where
    type AT a :: *
    a1f :: Ord (AT a) => a -> AT a
    a2f :: a -> a -> Bool
    a2f x y = a1f x >= a2f y

instance A Ta where
    type AT Ta = Integer
    a1f (_, y) = y
...

它会显示错误:

    • Could not deduce (Ord (AT a)) arising from a use of ‘>=’
      from the context: A a
        bound by the class declaration for ‘A’ at test.hs:10:15
    • In the expression: a1f x >= a1f y
      In an equation for ‘a2f’: a2f x y = a1f x >= a1f y

【问题讨论】:

forall 并不意味着随机。这意味着the caller chooses。在这种情况下,调用者是func2,你必须告诉它如何选择。你能多说一点你想要完成的事情,以便我们可以就如何解决这个问题提供更好的建议吗?有几种(矛盾的)前进方式,目前尚不清楚哪种方式最适合您。 @DanielWagner 我的整个程序有很多行,这只是我其他课程的练习。我在问题中添加了一个示例,这是我所拥有的最小化问题。你想给我一些建议吗? 你不需要向我们展示你的整个程序来解释你想要完成什么,但你确实需要谈论你想要实现的计算,以及你为什么相信这会引导你不可避免地需要具有这种形状的类型类来实现该计算。 【参考方案1】:

对您的类型族代码进行编译的最小修复是将Ord 约束的需求从产生它的地方移到消费它的地方:

-# LANGUAGE TypeFamilies #-
-# LANGUAGE TypeSynonymInstances #-
-# LANGUAGE FlexibleContexts #-
-# LANGUAGE FlexibleInstances #-
-# LANGUAGE ConstrainedClassMethods #-

type Ta = (Integer, Integer)

class Eq a => A a where
    type AT a :: *
    a1f :: a -> AT a
    a2f :: Ord (AT a) => a -> a -> Bool
    a2f x y = a1f x >= a1f y

instance A Ta where
    type AT Ta = Integer
    a1f (_, y) = y

如果您只想在使用默认实现时要求Ord (AT a),则可以使用DefaultSignatures(并消除ConstrainedClassMethods):

-# LANGUAGE TypeFamilies #-
-# LANGUAGE TypeSynonymInstances #-
-# LANGUAGE FlexibleContexts #-
-# LANGUAGE FlexibleInstances #-
-# LANGUAGE DefaultSignatures #-

type Ta = (Integer, Integer)

class Eq a => A a where
    type AT a :: *
    a1f :: a -> AT a
    a2f :: a -> a -> Bool
    default a2f :: Ord (AT a) => a -> a -> Bool
    a2f x y = a1f x >= a1f y

instance A Ta where
    type AT Ta = Integer
    a1f (_, y) = y

但是,这种类型类结构非常奇怪和单一。 (当我读到它时,它引发了一些危险信号:Eq 约束在那里做什么?为什么只有一个实例的类?为什么a2f 在类内部而不是外部?为什么不是a1f只是一个非类多态函数?为什么我们应该相信每种类型只有一个规范选择函数?)

我想重申,您应该告诉我们更多关于您试图通过此实现的目标,而不是谈论您为实现该目标而提议的类型类。这种架构的很多内容都在尖叫“初学者尝试像 OO 语言使用类的方式一样使用类型类”,这将成为阻抗不匹配和剪纸的持续来源。我强烈怀疑你根本不应该定义一个新的类型类。

【讨论】:

感谢您的建议。我已经搜索了很多关于“什么是类型类”的内容,并认为我在这里不需要它,只是用一些函数编写数据结构而不定义类型类。【参考方案2】:

就目前的代码而言,问题只是func b `op` func c 中的c模棱两可的。这并不是什么大问题:只需使用本地签名确定选择即可。例如

func2 x y = func x `op` (func y :: Int)

但这可能不是你真正想要的。 c 真的应该是 func 类的类型参数,还是整个实例的类型参数?在后一种情况下,MPTC 将是正确的方法。

-# LANGUAGE MultiParamTypeClasses, AllowAmbiguousTypes, TypeApplications #-

class ∀ b c . (A b, C c) => B b c where
  func :: b -> c
  func2 :: b -> b -> Bool
  func2 x y = func @b @c b `op` func c

或者,如果对于每个实例,只有一个 c 有意义,那么您需要一个类型族或fundep。

-# LANGUAGE TypeFamilies #-

class A b => B b where
  type Ct b :: *
  func :: b -> Ct b
  func2 :: b -> b -> Bool
  func2 x y = func b `op` func c

【讨论】:

抱歉我的描述不完整,我已经添加了我想要的示例。 func2 中的 op 在类型类中定义。如果我使用 typefamilies,则约束为miss,op 无法匹配。

以上是关于具有约束参数功能的类型类的主要内容,如果未能解决你的问题,请参考以下文章

C#泛型约束

泛型约束

请教一个unity有关于泛型参数的问题

c++ 模板

编写一个Car类,具有String类型的属性品牌,具有功能drive;

即使参数具有非空约束,也会收到有关可空类型参数的错误