Haskell 定义一个函数,该函数根据参数是整数还是分数定义不同的行为

Haskell 定义一个函数,该函数根据参数是整数还是分数定义不同的行为,haskell,ghc,Haskell,Ghc,一种解决方法是手工写出一堆实例 class Halve a where halve :: a -> a instance Halve Int where halve = (`div` 2) instance Halve Integer where halve = (`div` 2) instance Halve Float where halve = (/ 2) instance Halve Double where halve = (/ 2) -- ... etc.

一种解决方法是手工写出一堆实例

class Halve a where
  halve :: a -> a

instance Halve Int where
  halve = (`div` 2)
instance Halve Integer where
  halve = (`div` 2)
instance Halve Float where
  halve = (/ 2)
instance Halve Double where
  halve = (/ 2)
-- ... etc.
但是,不可避免地会遗漏一些实例,例如用户定义的实例。我尝试的真正解决方案是以下非工作代码:

{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}

class Halve a where
  halve :: a -> a

instance Integral a => Halve a where
  halve = (`div` 2)
instance Fractional a => Halve a where
  halve = (/ 2)
这就产生了错误

Duplicate instance declarations:
  instance Integral a => Halve a
    -- Defined at ...
  instance Fractional a => Halve a
    -- Defined at ...
如果可以的话,我还没有找到正确的表达方法。

你不能:

data Hell = YouCantDoThat

instance Integral Hell

instance Fractional Hell
没有规则规定积分不能是分数,反之亦然。如果是这样,你怎么选择?
这正是错误告诉您的。

您所要求的非常可怕。这门课是什么意思?用比工作部分解决方案少得多的样板文件也不可能做到这一点。问题是,即使是重叠或不一致(barf)实例,GHC也总是基于
=>
右侧的实例头部分提交给实例


GHC从不认为类型不是类的实例,只是为了产生错误消息。请尝试将值减半的函数作为代码的特定输入

foobar :: (Ord x) => (x -> x) -> x -> x -> ...
foobar halve x1 x2 = ...
这有点烦人,但希望您只需在算法的最顶层指定一次。任何子函数都可以只接收传递的参数

另一种选择是做如下事情

data Halvable x = Halvable {value :: x, halve :: x -> y}

halfInt :: Integral x => x -> Halvable x
halfInt x = Halvable x (`div` 2)

halfFrac :: Fractional x => x -> Halvable x
halfFrac x = Halvable x (/ 2)

一种轻量级方法是定义两种新类型,因此:

newtype FracWrap a = F a deriving Fractional
newtype IntWrap  a = I a deriving Integral

instance Fractional a => Halve (FracWrap a) where halve = (/2)
instance Integral   a => Halve (IntWrap  a) where halve = (`div`2)
然后,对于采用
Halve
约束参数的
foo
,我们使用相对轻量级的
foo.F
foo.I
来指示是否要使用
foo
分数或
积分版本


这在API设计方面是比较好的,因为API设计师不需要提供所有内容的两个版本;只有一个
将所有内容的特定版本减半,以及两个适配器。

@CNJ是的,但在这种情况下,您实际上希望在歧义中使用冲突。如果从代码中删除
整数
分数
的所有引用,则使用
Hell
数据类型不会出现任何错误,这相当于您所说的导入和冲突名称。问题是:您又开始使用样板代码了。@CNJ,一旦您加入了多参数类型类、函数依赖项、灵活上下文、灵活实例、不可判定实例和等式约束(所有这些都是现代Haskell编程所必需的),Haskell的类系统几乎达到了程序员使用和推理能力的极限。在一个系统不再有用之前,它的复杂程度是有限制的。@CNJ,不管是那样还是在类系统之外。@CNJ,不,不是这样的。但我觉得您可能能够重新构造代码,将这种区别推到更高的级别(可能会导致积分参数为偶数的不变量)。实际上,对于一个类来说,这并不是一个合适的用法。Haskell类应该真正代表特定的、行为良好的想法,而不仅仅是方便的重载。听起来你不是真的对数字减半感兴趣,而是对获取两个元素之间的轴心值感兴趣?这很合理。我认为你应该把它和这些数字类完全分开(因为这对角色来说也是有意义的)。告诉我们你的最终目标是什么会不会太痛苦?如果我们知道您真正想做什么,我们可能会更有效地帮助您。类型约束不足以决定实例。如果有人用整型和分数型实例定义了一个类型,该怎么办?您需要独特的类型构造函数,如下面建议的@daniel wagner