【问题标题】:Make functions an instance of vector type class使函数成为向量类型类的实例
【发布时间】:2012-10-24 18:08:23
【问题描述】:

我有一个用于数学向量的自定义类型类

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}

class Vector v a where

    infixl 6 <+>
    (<+>) :: v -> v -> v  -- vector addition

    infixl 6 <->
    (<->) :: v -> v -> v  -- vector subtraction

    infixl 7 *>
    (*>)  :: a -> v -> v  -- multiplication by a scalar

    dot   :: v -> v -> a  -- inner product

我想将数字a 和函数a -&gt; vector 放入该类的实例中。数字很​​简单:

instance Num a => Vector a a where
    (<+>) = (+)
    (<->) = (-)
    (*>)  = (*)
    dot   = (*)

我认为函数也很简单(好吧,除了dot,但我可以忍受)

instance Vector b c => Vector (a -> b) c where
    f <+> g = \a -> f a <+> g a
    f <-> g = \a -> f a <-> g a
    c *>  f = \a -> c *> f a
    dot     = undefined

但是,我收到以下错误:

Ambiguous type variable `a0' in the constraint:
  (Vector b a0) arising from a use of `<+>'
Probable fix: add a type signature that fixes these type variable(s)
In the expression: f a <+> g a
In the expression: \ a -> f a <+> g a
In an equation for `<+>': f <+> g = \ a -> f a <+> g a

我如何告诉 GHC 该实例对 所有 类型 a 有效?我应该在哪里添加类型签名?

【问题讨论】:

  • 这种方式可能是不可能的,毕竟Vector b c 与不同c 的所有实例可能具有&lt;+&gt; 的不同行为,即使c 实际上并没有出现在签名中的那个功能。 ——你是故意避开fundeps(或type family)还是单独开一个additiveGroup类? vectorspace 确实做到了两者,这肯定是有原因的。
  • 不,我不是故意避开它们。制作一个加法组类当然看起来很明智。我会调查基金并报告。

标签: haskell types typeclass


【解决方案1】:

类型族绝对是解决这个问题的最好方法

{-# LANGUAGE TypeFamilies, FlexibleContexts #-} 
class VectorSpace v where
    type Field v

    infixl 6 <+>
    (<+>) :: v -> v -> v  -- vector addition

    infixl 6 <->
    (<->) :: v -> v -> v  -- vector subtraction

    infixl 7 *>
    (*>)  :: Field v -> v -> v  -- multiplication by a scalar

    dot   :: v -> v -> Field v  -- inner product

从数学上讲,要使用函数创建向量空间,您必须重用相同的字段:

instance VectorSpace b => VectorSpace (a -> b) where
    type Field (a -> b) = Field b
    f <+> g = \a -> f a <+> g a
    f <-> g = \a -> f a <-> g a
    c *>  f = \a -> c *> f a
    dot     = error "Can't define the dot product on functions, sorry."

...关于类型族的好处是它们的工作方式与您解释的方式非常相似。 让我们做两个向量空间的直接乘积:

instance (VectorSpace v,VectorSpace w, Field v ~ Field w,Num (Field v)) => VectorSpace (v,w) where
    type Field (v,w) = Field v
    (v,w) <+> (v',w') = (v <+> v',w <+> w')
    (v,w) <-> (v',w') = (v <-> v',w <-> w')
    c *> (v,w) = (c *> v, c*> w)
    (v,w) `dot` (v',w') = (v `dot` v') + (w `dot` w')

您可以用自定义代数类替换 Num 上下文,但 Num 捕获了这个概念 场的中等水平。

【讨论】:

  • 谢谢。这与我阅读矢量空间包的源代码后编写的代码非常相似!
  • 我想我也应该读过它。 Conal 使dot 成为一个单独的类的一部分,因此并非所有的 Vector Speces 都必须有一个。这很有意义,因此您的代码中没有 dot = undefined
  • 是的,为 AdditiveGroup、VectorSpace 和 InnerSpace 设置单独的类绝对是有意义的。干得好,康纳尔。
【解决方案2】:

我能够编译以下小示例(以 Conal Elliott 的 vector-space 包为模板)编译:

{-# LANGUAGE TypeFamilies #-}

module Main
       where

class Vector v where
  type Scalar v

  infixl 6 <+>
  (<+>) :: v -> v -> v  -- vector addition

  infixl 7 *>
  (*>)  :: (Scalar v) -> v -> v  -- multiplication by a scalar

instance Vector v => Vector (a -> v) where
  type Scalar (a -> v) = (a -> Scalar v)
  f <+> g = \a -> f a <+> g a
  (*>) c f  = \a -> c a *> f a -- Can't deduce that Scalar v ~ Scalar (a -> v)

也许可以通过函数依赖而不是类型族来实现这一点。

【讨论】:

  • 谢谢 - 我刚刚自己阅读了向量空间的源代码,打算试试 TypeFamilies :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 1970-01-01
  • 2015-05-29
  • 1970-01-01
相关资源
最近更新 更多