最近,我尝试为2d vectors实现linear maps类。它很好,直到我尝试将vectors的基本类型(base-ring)更改为通用类型。我有以下类型的vectors:
data Vector a = V a a
字符串
(As据我所知,我不能或不应该指定a
是什么。
我想有一个线性Map的类(而不是类型),其背后的原因是:我想有一个非常特定的线性Map,比如旋转,它依赖于不同的参数集(甚至可能不依赖于任何参数,例如x反射没有参数),但它们都有一个目的-应用于向量(这就是为什么我称之为类)。
我尝试定义以下类
class LinearMap m where
apply :: m -> Vector a -> Vector a
型
我想要两个例子
data Rotation a = Rot a
instance LinearMap (Rotation a) where
Rot phi `apply` V x y = V (c * x - s * y) (s * x + c * y)
where
(c, s) = (cos phi, sin phi)
型
但它给了我一个错误,因为我期望的是一个类型,
apply :: Rotation a -> Vector a -> Vector a
型
而编译器预期
apply :: Rotation a -> Vector b -> Vector b
型
即使类型绑定不是问题,如下所示
data Reflection = XRefl | YRefl
instance LinearMap Reflection where
XRefl `apply` V x y = V (-x) y
YRefl `apply` V x y = V x (-y)
型
我得到一个错误,因为现在x
和y
必须是Num
的示例,但我不知道应该在哪里写示例。
一个简单的选择是编辑LinearMap
类并添加类似于
class LinearMap m where
apply :: Num a => m -> Vector a -> Vector a
型
但这不是我想要做的(我希望您能看到我的动机),而且它也没有解决Rotation
问题。
UPD:这是我的想法(不太高兴)
class LinearMap m a | m -> a where
apply :: m -> Vector a -> Vector a
型
现在Rotation
很好:
instance Floating a => LinearMap (Rotation a) a where
-- apply = ...
型Reflection
需要更新
data Reflection a = XRefl | YRefl
型
所以我可以定义一个示例
instance Num a => LinearMap (Reflection a) a where
-- apply = ...
型
因为a
* 依赖于m
,就像在LinearMap
类定义中一样。但是现在我不高兴Reflection
依赖于某些东西(它不应该对吗?)。
2条答案
按热度按时间9q78igpj1#
方法一:使用类型族和约束
这可能不是最优雅的方法,但是.
这是类:
字符串
这里,
CMap m a
是一个约束,定义了apply
工作所需的条件。型
对于
Rotation a
,我们需要a
与Vector a
中的a
相同,因此我们需要类型相等a ~ a'
。我们还需要Floating
,以便可以使用cos
。型
对于
Rotation
,我们只需要Num
。任何数字a'
都是允许的。方法二:去掉fundep(?)
我现在想知道是否在类型类中使用两个参数会更好,就像在
LinearMap m a
中一样,并将约束移动到示例中。我们只是避免了fundepm -> a
。型
我错过的这个简单的方法有什么问题吗?我们在类型推断过程中没有从x1m17 n1x推断出x1m16 n1x,因为我们缺乏fundep。这是一个真实的问题吗?
ukqbszuj2#
据我所知,我不能或不应该指定
a
是什么你绝对可以指定
a
是什么。你是否应该是另一回事。大多数Haskell社区使用linear
包,这确实使标量类型完全多态。IMO这没有什么意义,因为只有很少的类型在实践中作为标量有意义,而向量类型是完全参数化的函子是不明智的(因为在概念上,基的选择是任意的,但是fMap非线性函数破坏了对称性)。所以what I advocate for实际上是使用 monomorphic vector类型,或者至少将它们视为monomorphic:
字符串
同样,一个给定的线性Map示例将特定于一个具体的向量类型。有两种表达方式:
型
这是我最清楚的,但它意味着每个线性Map类型都有一个特定的域和共域,特别是你不能让
Reflection
作用于不同类型的域。这和你的函数依赖是一样的。型
尽管将向量空间视为非参数实体,但在这两种情况下都可以很好地交换标量类型。
型
在这个例子中,两种方法基本上是等价的。在反射的情况下,它们是不同的:
型
旁注:你所问的都与线性Map无关。你的例子实际上是 * 群作用于流形 * 的具体情况。例如,在球面上使用旋转是很有意义的。这是另一个动机,不要使用标量参数向量类型,而是表示拓扑空间的抽象类型。我也是have a package going in that direction。