如何在Haskell(ad-hoc多态)中重载函数乘以[Double]?

时间:2015-10-17 09:54:48

标签: haskell overloading

在Haskell中使用ad-hoc多态(函数重载)的方法是通过类型类(参见thisthisthis问题的答案等)。

但是我很难为以下情况定义一个重载的mult(产品)函数:

mult: [Double] -> Double -> [Double]
mult: Double -> [Double] -> [Double]
mult: [Double] -> [Double] -> [Double]

由于

(至少,案例1 [Double]*Double和案例3 [Double]*[Double]是必要的。)

2 个答案:

答案 0 :(得分:1)

与往常一样,我在尝试(没有成功)这个"并不像你想的那样有用:你包含你的代码很好,但如果你从编译器收到错误信息,告诉我们它是什么!他们非常有教育意义,并且出于某种原因打印出来。

我刚试过你写的东西,这实际上是你(可能)得到的错误信息:

*Multiplication> mul 1 [2]

Non type-variable argument
  in the constraint: Multipliable ta [t] tc
(Use FlexibleContexts to permit this)
When checking that ‘it’ has the inferred type
  it :: forall ta tc t. (Num ta, Num t, Multipliable ta [t] tc) => tc

现在,您可以尝试启用FlexibleContexts,但这似乎无法解决问题。但是,正如编译器告诉您它在推断类型时遇到问题时通常的情况一样,您应该尝试添加一些显式类型,看看是否有帮助:

*Multiplication> mul (1::Double) [2 :: Double]
[2.0]

基本上,编译器无法确定您想要的mul重载:12是多态的,可以是任何数字类型,而且只有mul 现在的一个合适的重载,编译器不进行这样的推断,除非它可以证明在这种情况下不会存在其他重载。完全指定参数类型足以解决问题。

解决此特定问题的另一种方法是为每个参数使用类型类,将其转换为规范类型[Double],而不是整个参数的类型类。这是一个比一般ad hoc多态更具体的解决方案,并不是所有问题都适合,但对于像处理数字列表这样的单个数字这样的事情应该没问题:

module Multiplication where
import Control.Monad (liftM2)

class AsDoubles a where
  doubles :: a -> [Double]

instance AsDoubles Double where
  doubles = return

instance AsDoubles [Double] where
  doubles = id

mult :: (AsDoubles a, AsDoubles b) => a -> b -> [Double]
mult x y = liftM2 (*) (doubles x) (doubles y)

*Multiplication> mult [(1 :: Double)..5] [(1 :: Double)..3]
[1.0,2.0,3.0,      -- whitespace added for readability
 2.0,4.0,6.0,
 3.0,6.0,9.0,
 4.0,8.0,12.0,
 5.0,10.0,15.0]

答案 1 :(得分:1)

我设法这样做了。当然不是很好。

我认为任何人都应该考虑左下方的评论和评论,我为了方便和相关而在下面引用。

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances #-}

class Multipliable ta tb tc | ta tb -> tc where
      mul :: ta -> tb -> tc

instance Multipliable [Double] Double [Double] where
          mul p k = map (*k) p --mul p k = map (\a -> k * a) p

instance Multipliable Double [Double] [Double] where
          mul k p = map (*k) p --mul p k = map (\a -> k * a) p

instance Multipliable [Double] [Double] [Double] where
         mul p q = p  -- dummy implementation


r = [1.0, 2.0, 3.0]  :: [Double]


r1 = (mul :: [Double] -> Double -> [Double]) r 2.0 

r2 = (mul :: Double -> [Double] -> [Double]) 2.0 r

r3 = (mul :: [Double] -> [Double] -> [Double]) r1 r2


main = do
     print r1
     print r2
     print r3
  

为什么你还想要这个呢?仅仅因为Matlab允许乘法   你抛出的任何东西并不意味着这是一个好主意。查看   矢量空间正确处理   多维-乘法。或者,如果你不在乎   很多数学优雅,你可以使用hmatrix(事实上   很像Haskell中的Matlab / Octave),或线性的。

     

我认为一般来说这是一个坏主意,在Haskell中真的没用,因为你可以写map (*x) yszipWith (*) xs ys   让你明确意图。这当然不起作用   应该处理标量和向量的多态代码 -   但是,编写这样的代码只是处理标量或任何列表   相反,长度是在寻找麻烦。指定哪个是很尴尬的   list需要有一个长度匹配哪个其他列表和长度   结果将是等等。这是矢量空间或线性光泽,   因为他们在编译时检查尺寸。