为不同的数据类型构造函数使用不同的最小完整定义

时间:2018-05-23 17:36:06

标签: haskell typeclass

我有一个类型Transforming,有两个完整的定义applyfill。这是:

import Data.Set (Set, foldr, union, empty, map, singleton)
import Prelude hiding (foldr, map)

class Transforming t where
  apply :: (Ord a) => t a -> a -> Set a
  apply trans = fill trans . singleton
  fill  :: (Ord a) => t a -> Set a -> Set a
  fill trans = foldr union empty . map (apply trans)

(确切的细节或多或少不重要,但我想我会把它们包括在内)

现在我设置它来推断成员函数的方式很好,但通常是天真的。因此,如果我正在创建Transforming的实例,我可能希望根据最有效的方式实现applyfill,并让Haskell处理另一个。 (或者在极少数情况下实现两者)这很好,这是一个类型类的点。但是,此时我想创建一个新的数据类型如下

data SimpleTransform a = SingletonTrans (a -> a) | GenericTrans (a -> Set a)

现在,当SimpleTransformTransforming实例时,我希望为使用fill构建的实例实现SingletonTrans,并为使用{构造的实例实现apply {1}}。我想写这样的东西:

GenericTrans

然而,这不起作用。由于instance Transforming Transform where fill (SingletonTrans f) = map f apply (GenericTrans f) = f 是单个数据类型,我需要完全定义两个最小定义中的一个,而不是部分地定义它们。现在有办法解决这个问题。最简单的是完全定义两个实例,但这是一个非常简单的例子,随着我的类型类中完整定义的数量增加,构造函数的数量增加,这开始需要重复大量的代码。

有没有办法可以为同一数据类型的不同构造函数使用不同的完整定义?有什么方法我应该重构我的代码,以使这不必要吗?

2 个答案:

答案 0 :(得分:5)

这是一种方式:

applyDef trans = fill trans . singleton
fillDef trans = foldr union empty . map (apply trans)

class Transforming t where
  apply :: (Ord a) => t a -> a -> Set a
  apply = applyDef
  fill  :: (Ord a) => t a -> Set a -> Set a
  fill = fillDef

instance Transforming Transform where
  fill (SingletonTrans f) = map f
  fill trans = fillDef trans
  apply (GenericTrans f) = f
  apply trans = applyDef trans

答案 1 :(得分:5)

另一种方法,使用Data.Functor.Sum

newtype SingletonTrans' a = SingletonTrans' (a -> a)
instance Transforming SingletonTrans' where
  fill (SingletonTrans' f) = map f

newtype GenericTrans' a = GenericTrans' (a -> Set a)
instance Transforming GenericTrans' where
  apply (GenericTrans' g) = g

instance (Transforming f, Transforming g) => Transforming (Sum f g) where
  fill (InL f) = fill f
  fill (InR g) = fill g
  apply (InL f) = apply f
  apply (InR g) = apply g

type SimpleTransform = Sum SingletonTrans' GenericTrans'

您甚至可以使用PatternSynonyms使SimpleTransform行为与原始定义相同:

pattern SingletonTrans :: (a -> a) -> SimpleTransform a
pattern SingletonTrans f = InL (SingletonTrans' f)

pattern GenericTrans :: (a -> Set a) -> SimpleTransform a
pattern GenericTrans g = InR (GenericTrans' g)