如何在 Haskell 中重载用于乘以 [Double] 的函数(临时多态性)?

How to overload a function for multiplying [Double] in Haskell (ad-hoc polymorphism)?

在 Haskell 中实现临时多态性(函数重载)的方法是通过类型 类(参见 , and this 问题的答案等)。

但我正在努力为以下情况定义重载的 mult(产品)函数:

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

谢谢

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

一如既往,像 "I'm trying (with no success) this" 这样的语句并没有您想要的那么有用:包含您的代码很好,但是如果您收到来自编译器的错误消息,告诉我们它是什么!它们非常有启发性,印刷是有原因的。

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

*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 now 的重载,编译器不会做出这样的推断,除非它可以证明在此上下文中不存在其他重载。完全指定参数类型足以解决问题。

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

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]

我已经成功做到了。当然不是很好。

我认为任何人都应该考虑 leftaroundaobut 对这个问题的评论和批评,为了方便和相关性,我在下面引用了这些评论和评论。

{-# 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

Why do you want this anyway? Just because Matlab allows multiplying anything you throw at it doesn't mean this is a good idea. Check out vector-space for properly dealing with multidimensional-multiplications. Alternatively, if you don't care so much for mathematical elegance, you can use hmatrix (which is in fact a lot like Matlab/Octave in Haskell), or linear.

I think it's a bad idea in general, and really unnecessary in Haskell because you can just write map (*x) ys or zipWith (*) xs ys to make you intent explicit. This of course doesn't work for polymorphic code that's supposed to handle both scalars and vectors – however, writing such code to just deal with scalars or lists of any length is rather asking for trouble. It's awkward to specify which list needs to have a length matching which other list and what length the result will be etc.. This is where vector-space or linear shine, because they check dimensions at compile time.