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

时间:2019-09-23 11:55:32

标签: haskell typeclass forall

我想写一个像这样的类:

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看起来不错,但不能像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

2 个答案:

答案 0 :(得分:1)

在您现有的代码中,问题很简单,c中的func b `op` func 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

答案 1 :(得分: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在类中而不是外部?为什么呢? t a1f只是一个非类多态函数?为什么我们认为每种类型只有一个规范选择函数?)

我想重申一下,您应该向我们详细说明您正在尝试实现的目标 ,而不是谈论为实现该目标而建议的类型类。关于这种体系结构的很多东西都在尖叫“初学者尝试使用OO语言使用类的方式来使用类型类”,这将成为您不断出现的阻抗失配和剪纸的来源。我强烈怀疑您根本不应该定义一个新的类型类。