为多种类型定义函数

时间:2016-09-21 23:39:58

标签: haskell types

如何在Haskell中为不同类型定义函数? 给定

func :: Integral a => a -> a
func x = x
func' :: (RealFrac a , Integral b) => a -> b
func' x = truncate x

如何将它们组合成一个带有签名的函数

func ::  (SomeClassForBoth a, Integral b) => a -> b

3 个答案:

答案 0 :(得分:4)

使用类型类。

class    TowardsZero a      where towardsZero :: Integral b => a -> b
instance TowardsZero Int    where towardsZero = fromIntegral
instance TowardsZero Double where towardsZero = truncate
-- and so on

可能一个具有相关类型系列约束的类更接近您所写的内容(尽管可能不会更接近您的想法):

{-# LANGUAGE TypeFamilies #-}
import GHC.Exts

class TowardsZero a where
    type RetCon a b :: Constraint
    towardsZero :: RetCon a b => a -> b

instance TowardsZero Int where
    type RetCon Int b = Int ~ b
    towardsZero = id

instance TowardsZero Double where
    type RetCon Double b = Integral b
    towardsZero = truncate

-- and so on

答案 1 :(得分:2)

我不建议这样做,但你可以用GADT破解它:

data T a where
    T1 :: a -> T a
    T2 :: RealFrac a => a -> T b

func :: Integral a => T a -> a
func (T1 x) = x
func (T2 x) = truncate x

T类型说:“要么你已经知道我正在包装的值的类型,要么它是RealFrac的一些未知实例”。 T2构造函数存在量化a并打包RealFrac字典,我们在func的第二个子句中使用该字典将(未知)a转换为b。然后,在func中,我将Integral约束应用于aT可能在SRCFILES := $(wildcard */*.c) EXECFILES := $(join $(addsuffix executables/,$(dir $(SRCFILES))), $(basename $(notdir $(SRCFILES)))) 内,也可能不在dir1/executables/q1)内。

答案 2 :(得分:2)

这称为 ad hoc多态,您可以根据类型执行不同的代码。在Haskell中完成此操作的方法是使用类型类。最直接的方法是定义一个新类

class Truncable a where
    trunc :: Integral b => a -> b

然后你可以定义几个具体的实例。

instance Truncable Integer where trunc = fromInteger
instance Truncable Double where trunc = truncate

这是不满意的,因为当实际上只有两个外观相同的实例时,它需要每个具体类型的实例。不幸的是,由于技术原因(能够像这样定义“实例族”干扰类型推断的开放世界假设,以及类型推断的其他困难),这是难以减少样板的情况之一。作为复杂性的提示,请注意您的定义假定没有RealFracIntegral类型,但这不能保证 - 在这种情况下我们应该选择哪种实现?

此类型类解决方案还有另一个问题,即Integral版本没有类型

trunc :: Integral a => a -> a

如您所指定,而是

trunc :: (Integral a, Integral b) => a -> b

在语义上这不是问题,因为我不相信有可能最终得到一些多态代码,你不知道你正在使用的类型是Integral,但你确实需要要知道它是什么时候,结果类型与传入类型相同。也就是说,我声称无论何时您需要前者而不是后者签名,您都已经知道足以在源中替换trunc id。 (虽然这是一种直觉,我希望被证明是错误的,似乎是一个有趣的谜题)

然而,可能存在性能影响,因为您可能会不必要地调用fromIntegral将类型转换为自身,我认为解决这个问题的方法是使用{-# RULES #-} definitions,这是一个黑暗可怕的包我从未真正挖过的复杂性,所以我不知道这有多么难或多么容易。