【问题标题】:New type class with instance definition generic on tuples of any length在任意长度的元组上具有通用实例定义的新类型类
【发布时间】:2017-01-16 18:51:19
【问题描述】:

是否可以使用 GHC 扩展来定义一个新的类型类来泛化为任意长度的元组?

关于 Prelude 和 Base 中内置类的行为(有些类支持多达 15 个元素的元组,有些多达 7 个)以及扩展这些类的(非)可能性,已经存在一些问题。

前奏和基本行为: Haskell Tuple Size Limit

用新定义扩展 Show: Extend a Show instance to a Tuple of any size

我问的是一个稍微不同的问题。如果我正在制作一个全新的类型类,是否可以添加一个实例规则来处理任意长度的元组(可能使用 GHC 扩展)?

这是一个名为 PartialOrder 的类的示例。我想允许使用以下规则对任意大小的元组进行部分比较

(a,b ... , z) <= (a1,b1, ... , z1) iff (a <= a1) && (b <= b1) && ... && (z <= z1)

这是我第一次尝试使用传统的“为不超过任意大小的元组定义类”方法进行定义。

是否有可用于编写涵盖任意长度元组的实例定义的 GHC 扩展?

我想我可以使用 Template Haskell 或外部程序提前生成定义,但不能像 C++ 模板那样按需生成。

-- Sets equipped with the (is_subset) operation are an example of a
-- partial order.
--
-- {} < {a}      less than
-- {} = {}       equal to
-- {a, b} > {b}  greater than
-- {a} ~ {b}     incomparable
--
-- in order to define a partial order we need a definition of (<=)

data PartialOrdering = POLessThan | POGreaterThan | POEqual | POIncomparable deriving (Eq, Show)

class PartialOrder a where
    lessThanEq :: a -> a -> Bool

instance PartialOrder PartialOrdering where
    lessThanEq POIncomparable _ = False
    lessThanEq _ POIncomparable = False

    -- with incomparables dealt with...
    lessThanEq POLessThan _ = True

    lessThanEq POEqual POLessThan = False
    lessThanEq POEqual _ = True

    lessThanEq POGreaterThan POGreaterThan = True
    lessThanEq POGreaterThan _ = False


-- note this is different from the semantics for Ord applied to tuples,
-- which uses lexicographic ordering.
--
-- (a,b) is less than or equal to (c,d) iff
-- a <= b and c <= d

-- 2 element tuple
instance (PartialOrder a, PartialOrder b) => PartialOrder (a, b) where
    lessThanEq (a,b) (c,d) = (lessThanEq a c) && (lessThanEq b d)

-- 3 element tuple
instance (PartialOrder a, PartialOrder b, PartialOrder c) => PartialOrder (a, b, c) where
    lessThanEq (a,b,c) (d,e,f) = (lessThanEq a d) && (lessThanEq b e) && (lessThanEq c f)

-- 4 element tuple
instance (PartialOrder a, PartialOrder b, PartialOrder c, PartialOrder d) => PartialOrder (a, b, c, d) where
    lessThanEq (a,b,c,d) (e,f,g,h) = (lessThanEq a e) && (lessThanEq b f) && (lessThanEq c g) && (lessThanEq d h)


-- etc.


main = putStrLn "hi"

【问题讨论】:

  • 只是一个建议:为什么不使用长度索引列表?这种数据类型与元组同构。使用 GADT 很容易定义它。

标签: haskell


【解决方案1】:

Haskell 中的元组类型并不真正了解彼此。值得庆幸的是,对于您的特殊情况,您可以使用 GHC.Generics 解决您的问题。然后,您实际上将能够为 任何 产品类型派生您的 PartialOrder 类,而不仅仅是元组。

{-# LANGUAGE TypeOperators, DefaultSignatures, FlexibleContexts, 
             StandaloneDeriving, DeriveAnyClass
  #-}

import GHC.Generics
import Data.Function (on)

data PartialOrdering
  = POLessThan | POGreaterThan | POEqual | POIncomparable deriving (Eq, Show)

class PartialOrder a where
  lessThanEq :: a -> a -> Bool

  default lessThanEq :: (Generic a, GPartialOrder (Rep a)) => a -> a -> Bool
  lessThanEq = gLessThanEq `on` from


-- | Helper generic version of your class
class GPartialOrder f where
  gLessThanEq :: f a -> f a -> Bool

-- | Product types
instance (GPartialOrder a, GPartialOrder b) => GPartialOrder (a :*: b) where
  gLessThanEq (a1 :*: b1) (a2 :*: b2) = gLessThanEq a1 a2 && gLessThanEq b1 b2

-- | Unary type (empty product)
instance GPartialOrder U1 where
  gLessThanEq U1 U1 = True

-- | Meta information on type
instance (GPartialOrder a) => GPartialOrder (M1 i c a) where
  gLessThanEq (M1 a1) (M1 a2) = gLessThanEq a1 a2

-- | Single type
instance (PartialOrder a) => GPartialOrder (K1 i a) where
  gLessThanEq (K1 x1) (K1 x2) = lessThanEq x1 x2

通过所有这些设置,如果派生Generic(可以使用-XDeriveGeneric),则可以自动派生您的类(启用-XDeriveAnyClass)。元组类型已经是泛型的实例,因此使用-XStandaloneDeriving,您可以追溯派生Partial Order 的实例。以下所有工作

deriving instance (PartialOrder a, PartialOrder b) => PartialOrder (a,b)
deriving instance (PartialOrder a, PartialOrder b, PartialOrder c) => PartialOrder (a,b,c)
-- and so on...

data MyProduct a b = MyProduct a b deriving (Generic, PartialOrder) 

之后,您可以按预期使用您的课程:

ghci> (POLessThan, POLessThan) `lessThanEq` (POEqual, POEqual)
True
ghci> (POLessThan, POEqual) `lessThanEq` (POEqual, POEqual)
True
ghci> (POLessThan, POEqual) `lessThanEq` (POEqual, POLessThan)
False

【讨论】:

    猜你喜欢
    • 2012-05-06
    • 1970-01-01
    • 2022-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-14
    • 1970-01-01
    相关资源
    最近更新 更多