有几种不同的方法可以实现这一点,但也有一些缺点。
以下是我正在使用的类型:
data A = A { a :: String}
data B = B { bA :: A, b :: Char}
data C = C { cA :: A, c :: Float}
Ad-hoc多态性:所有这些类型都可以使用
您可以将f
定义为CanF
类的一个方法,该类是A
、B
和C
的所有实例的公共类:
class CanF a where
f :: a -> Int
instance CanF A where
f = length . a
instance CanF B where
f = f . bA
instance CanF C where
f = f . cA
用A
的实例来定义B
和C
的实例,可以清楚地表明f
在每种情况下都执行相同的操作。根据定义F
实例的类型,很容易使f
执行不同的操作。这种方法的缺点是任何类似f
的其他函数都需要作为同一“CanSomething”类的方法添加。
main :: IO ()
main = do
print (f a)
print (f b)
print (f c)
where
a = A "Hello"
b = B a 'H'
c = C a 3.14
即时多态:所有这些类型都可以表示为A
另一种方法是将f
写为一个受类约束的函数,该类始终提供给你一个A
。
class RepA a where
getA :: a -> A
instance RepA A where
getA = id
instance RepA B where
getA = bA
instance RepA C where
getA = cA
f :: RepA a => a -> Int
f = length . a . getA
在这种情况下,你对定义f
的灵活性较少,这可能有好处也可能有坏处。优点是,你可以定义其他函数来处理A
,而不必向类中添加新方法。
函数记录
我喜欢使用函数记录方法来处理这个问题。定义一个参数化数据类型,其中包含要调用的函数。然后为你的记录类型定义专门的构造函数。这种方法的缺点是通常更冗长。优点是,通过提供不同的F
给f
函数,你可以交换行为。另一个优点是,在不需要语言扩展的情况下,你可以完成更多工作。
data F a = F { f :: a -> Int }
af :: F A
af = F $ length . a
bf :: F B
bf = F $ f af . bA
cf :: F C
cf = F $ f af . cA
main :: IO ()
main = do
print (f af a)
print (f bf b)
print (f cf c)
where
a = A "Hello"
b = B a 'H'
c = C a 3.14
data A = A { a :: String}
;data B = B { bA :: A, b :: Char }
;data C = C { cA :: A, c :: Float }
。 - bheklilr