我已经在使用类型运算符之前完成了此操作,但是我想排除那些,因为我希望能够用较小的锤子来完成,因为我实际上想用另一种语言来完成,而且我不太确定类型运算符完全可以满足我的要求。
设置有两种数据类型,整数和...
> data Rational = Rational Integer Integer deriving Show
两个具有明智实例的类型类...
> class Divide2 a where
> divide2 :: a -> a
> class Increment a where
> inc :: a -> a
> instance Increment Main.Rational where
> inc (Rational a b) = Rational (a + b) b
> instance Divide2 Main.Rational where
> divide2 (Rational a b) = Rational a (2 * b)
> instance Increment Integer where
> inc x = x + 1
我可以定义适用于一种类型类或另一种类型实例的东西
> div4 x = divide2 (divide2 x)
> add2 :: Increment c => c -> c
> add2 = inc . inc
然后我想将这两种数据类型结合起来...所以显而易见的事情是使用已区分的
> data Number = Rat Main.Rational | Int Integer
现在...在我的场景中,作用于该联合的函数存在于一个不同的模块(二进制文件,我对Haskells二进制文件不熟悉)
但是数据类型本身是在另一个中定义的
因此,很明显,我可以定义一些(原则上)可以在此联合上“起作用”的函数,例如作用于Increment实例值的函数....以及一些不起作用的函数,例如Divide2中的一个
那么我如何针对这个有区别的联合编写一个函数,将一个函数应用于联合中的值,该函数将针对Increment编译,但不会针对Divide2上的函数进行编译...走到这里,落在我的脸上。
> apply (Rat r) f = f r
> apply (Int i) f = f i
。
• Couldn't match expected type ‘Main.Rational’
with actual type ‘Integer’
• In the first argument of ‘f’, namely ‘i’
In the expression: f i
In an equation for ‘apply’: apply (Int i) f = f i
|
86 | > apply (Int i) f = f i | ^
Failed, no modules loaded.
正如预期的那样,推理表明它必须是Rational,因为它是第一个调用,但是它是Integer ...
但是“很明显” ...如果我可以让haskell怀疑置疑...像某种宏...然后使用功能
> apply (Int 1) add2
对我想选择的Number的任何值都有意义,而且有意义。
因此,显而易见的事情是使Number成为每个成员所在的类型类集合的交集中任何事物的成员。...
> instance Increment Number where
> inc (Rat r) = inc (Rat r)
> inc (Int i) = inc (Int i)
然后ghc自己实现“应用” ...我也可以通过一些显式的字典传递将该解决方案映射回其他语言...但是我有数百个(如果不是上千个)小类型类(我可能不得不还要考虑它们的所有组合)。
所以我真的很想知道是否存在某种类型魔术(现有的?rankn?),这意味着我可以针对Number编写“应用”,而无需求助于某些依赖类型魔术,或者必须在该类上实现类型类的实例。歧视工会。
P.S。我可以做有限的依赖类型魔术...但这是不得已的手段,
编辑...
包含在Number上定义的函数的代码当然可以匹配已区分的值,但是如果这样做,则只要扩展并集,它们都将无法编译(好吧,它们不必分别匹配每种情况,但是除非这样做,否则他们将无法提取包装的值来应用该函数,因为它不会知道类型)
嗯...写下来看起来像表达问题,实际上是表达问题...那时我知道很多解决方案...我只是通常不喜欢其中任何一个...让我使用类型类将规范的Haskell解决方案解决掉。
答案 0 :(得分:1)
这就是表达式问题,所以类型类可以解决这种特殊情况。
您采用了想要对一些尚未定义的类型的泛型通用的函数
> class Add2 a where
> add2' :: a -> a
> newtype Number' a = Number' a
> instance (Increment a) => Add2 (Number' a) where
> add2' (Number' x) = Number' $ inc (inc x)
> three = add2 (Int 1)
,然后根据类型类创建任何占用所需先决条件的类型,并使用您的广义“函数”的类型类。
然后您实现新的“ Number”数据类型,并在有意义的地方创建它们的实例。
答案 1 :(得分:1)
您只能接受使用Increment
方法(而不使用任何非Increment
al功能)的函数,如下所示:
{-# LANGUAGE RankNTypes #-}
apply :: (forall a. Increment a => a -> a) -> Number -> Number
apply f (Rat r) = Rat (f r)
apply f (Int i) = Int (f i)
如果愿意,您现在可以将add2
传递给apply
> apply add2 (Rat (Rational 3 4))
Rat (Rational 11 4)
在这种特定于 的情况下,实现apply
等同于为Increment
本身提供Number
实例:
instance Increment Number where
inc (Rat r) = Rat (inc r)
inc (Int i) = Int (inc i)
......现在您甚至不需要中介apply
函数即可应用add2
:
> add2 (Rat (Rational 3 4))
Rat (Rational 11 4)
但这是一个非常特殊的情况。仅为Number
实现适当的类并不总是那么容易,并且您将需要诉诸于apply
中使用的更高级别的类型。