如何在 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
的哪个重载:1
和 2
是多态的,可以是任何数字类型,而只有一个适合 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.
在 Haskell 中实现临时多态性(函数重载)的方法是通过类型 类(参见
但我正在努力为以下情况定义重载的 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
的哪个重载:1
和 2
是多态的,可以是任何数字类型,而只有一个适合 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
orzipWith (*) 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.